nice feature. Guess that comes from using Slack a lot.
I'm pretty jazzed about the possibilities of Observable, and Javascript puts a lot of power in the hands of the notebook author
Looking forward to checking out Jacob's Zome Math page too, but I think I'll be pretty busy thru Labor Day. We're having a pool party here on Sat & Mon, so I have some work to do to get ready in the next few days.
you've been awfully productive lately, so you get a vacation
Biggest hurdle for me with reusing javascript is finding modular examples of stuff and learning what libraries are available and then they have their own learning curves.
D3 is worth learning, I'd say... a very nice abstraction
I had heard about D3 for years, and thought, "Oh, a charting library", and imagined exactly the opposite of what it is
Yeah, D3 seems to be a pretty common factor.
I know you'll like it, just knowing your love of visualization
What do you think about adding the CORS header to the vzome server? Is that something you can do easily?
Probably... need to look into it. I was also wondering how much I'd have to pay to get a cert and go HTTPS
Being able to use a plain fetch would be nice.
Dreamhost, my hosting provider, usually has VERY good documentation about such things, and even better support people.
Yes... but a Gist would be easier, probably... and would be something we could build into vZome... Gist upload.
Oh, an idea I shared with Jacob, but you'll like it:
Many hostsing sites have very cheap SSL now-a-days.
here's the idea: a "copy to clipboard" tool that is configurable in terms of export format
want VEF? sure. JSON for Observable? no problem. STL, whatever
use the selection, or just the whole model if there is no selection
Of course, the polymorphic paste in vZome might not be easy... unless we can use clipboard metadata in a cross-platform way.
might need a new abstraction layer to support at least Mac and Windows
but I'm not worried about paste... we can require VEF there
I just want a smooth workflow for getting models into Observable
As long as they're all text based, I think it would be simple and wouldn't need any OS specific code.
you'd like to have a MIME type... so a recipient can say "JSON? No!"
I'm thinking of vZome paste... we'd require VEF only, or maybe the new VSON that also has exact numbers
clipboard doesn't care about mime types AFAIK
I know the Mac clipboard supports some type notion... just assumed it was MIME
Window can too. For example, I can copy formatted text (e.g. HTML) and paste it as HTML or as text, but as long as we keep it all as just text, the mime type (or equivalent) shouldn't matter.
with only three of us, channel discipline is not that important
helps a bit with discovery/search of old conversations
depends on how it's copied. If the copying app copies it as formatted text, or an image or plain text, or a link to a file, or whatever, the pasting app has to be able to paste it that format. Some apps copy in multiple formats and they the pasting depends on which the pasting app chooses, but if we stay with just text, then it's all good.
I can't remember seeing an app offer me a choice of paste type, so I guess the app usually determines what's "best"
Hi Jacob. Thanks for turning Scott (and me) on to Observable. Looks like fun.
true. most apps would decide based on the paste context. Chrome is one I know that gives the user the option to Paste, or Paste as plain text.
I think for vZome we'll keep it simple... we three are probably the only users that will ever use the "copy export" feature
@David Hall you maybe saw my hacked up prototyping https://beta.observablehq.com/@jrus/zome-arithmetic
yes, all the exports will be text... but could be JSON, VEF, STL, etc.
Scott sent me the link earlier tonight. I'm looking forward to checking it out - hopefully tomorrow.
the chinese government is already going to have a bunch of photos of simon via wechat
I appreciate both of you accommodating me... David and I had been using Google Hangouts primarily
Ouch. That would be painful. How old is he now?
you can change skins, if you don't like slate-purple or whatever
@jrus to catch you up a bit, I'm merging most of David's 8 (!) pull requests on vZome, and generally getting back in control of my local repo. That includes running full regression as I speak, just in case David made a bug (or I did).
I want to get to a stable spot so I can cut a build that has the new exporter.
by new exporter, you mean json, not clipboard, right?
I'm going to switch to TrackballControl... I should do that now
what is the most useful and effective UI for rotating with (a) mouse, and (b) multitouch
I still can't accept anything accept an analogue of the old Centipedes (etc) trackball
I told you about my 3D trackball project that never really got off the ground right?
I think so, yes. Not sure what part of the trackball frustrates you though...?
oh, most trackballs don't send quaternion input to the computer
oh, right... but are you OK with the way we map mouse drag to rotation in vZome?
although I do like the right-click pan capability of OrbitControl. I assume that TrackballControl has that too. It would be nice in vZome too.
@Scott I think there may be better ways to do rotation with a mouse
with a mouse or multitouch I want to see how it works to do an invisible stereographic projection
I can imagine playing with acceleration, momentum, or whatever, but I can't imagine a better way to map direction
hmmm... I think stereographic might feel like those controls where you lose "gain" as you get to the edge
with a stereographic projection you would need to do multiple drags for some rotations
I recall one of the first things I wanted to be able to do n vzome was lock the rotation around an axis. We just never came up with a good UI to lock or unlock it. Having the mouse controller map to just one direction of roation is the easy part.
we do that for "working plane mode" when dragging struts
shouldn't be hard to use the same MouseAdapter or whatever for drag... just tricky to define the axis (plane)
My idea was to rotate around just X or Y, but Z could be nice too, just a bit harder to implement.
these are the sorts of things that may already exist in threejs
If so, we could play with them there first and see which we really like rather than which we can imagine.
I'll leave Slack running in case y'all have anything I should see in the morning.
@jrus I've been trying to anticipate how you'll map the symmetry function to segment-sets
I was planning to just apply symmetry to each segment, and then discard duplicates
the noun I mentioned the other night... your basic "model"
so I just need to write a simple javascript "SegmentSet" class or whatever
OK, I'll understand when I see it. Still not up to speed on all the newer ES6 tricks
some missing features in IE, but supported everywhere else including edge
I'm not sure how to put mutable objects in there and have them compare properly etc.
will be important for me... vZome relies on hashsets quite a bit
Jacob, what's your email address? I have some ideas for your zome math page. I won't get to them til tomorrow though.
@David Hall jacob.rus@gmail, or me@jacobrus.com, or various others
@Scott so the main issue is that it seems arrays in a set are compared by identity, which doesn't really help as [1, 2, 3] and [1, 2, 3] are going to be different objects. not sure if there's a way around that or if I need to implement my own class built on Map
you can start with just accepting duplicates, perhaps... not sure what threejs will do, but it might only affect performance
with Java3d, overlapping polygons cause odd interference effects
but mostly only when I highlight one of them... otherwise you cannot tell
@Scott okay, this is the lamest type of deduplicator. just naively turns each item into a string https://beta.observablehq.com/@jrus/zome-arithmetic#deduplicate
yes, as I feared, Set is just based on ===, which does not do deep object / array equality
looks like lodash has a good equals()... maybe they have a Set class based on that
@Scott it's possible to make another class based on Map where internally the keys are some string and the values are the struts, but externally it looks just like a Set
@Scott but for most purposes I think it's fine to use a list and occasionally call this kind of dedup function
for a set, the way to test equality is via some kind of 'hash' function which produces the same string/number/etc. output for equal objects, not via an equals() method per se.
Top row of https://beta.observablehq.com/@jrus/wolfram-cellular-automaton is offset 😉
@David Hall also, that code is from a 12 year old, I didn't write it
I just put it there because he provided a downloadable nodejs project, and I thought people might want to see the code run right away
he made a hacker news post, linked from the top of that page
hmmm. I figured it should be a Serpinski triangle. I'll have to look a little closer.
it is a sierpinski triangle. it's just that you can't really have a halfway-offset cell in this type of cellular automaton
if you render it using pixels and zoom way out it won't be noticed
OK. I see now. All of the other complete horizontal lines have an even number of cells.
@Scott your vzome-rendering-component could use better names for the 'defaultShapes':)
and be happy that I went through and shrunk the IDs from 40 characters down to 8
@Scott btw, here's one with some cleaned up whitespace https://beta.observablehq.com/d/8cc4eb94a0adb9c9#defaultShapes
oh, sorry you went to that trouble... now I have a new set with better names
https://beta.observablehq.com/@vorth/vzome-rendering-component
also, I added the getGeometry() function to handle the fallback
and https://beta.observablehq.com/@vorth/vzomerenderer-component-demonstration no longer imports the fetcher notebook
The triacontahedron in that notebook was directly pasted in from vZome
I am planning to cut a build soon... or I can offer another export that gives you your segment-list format, also
is there a reason for the names of the blues to be multiples of 2 while red and yellow get names that are multiples of 1?
not a great reason... just using an existing function on my model object
I can tweak the naming, of course... this was quick and easy to produce
we can probably make some code in the not too distant future which generates these strut geometries
I already have them, though! They are all model-driven, of course
we can, of course, do a generic strut geometry... I should share that with you
oh, there's a better way... stretchable shapes. This is how I store them internally
or do you just draw those as multiples of standard zome struts?
no, my shape definitions have designated vertices that get scaled out to match the strut endpoint
I can also support half-scaled geometry bits... to do the antiprisms in the middle of traditional Zome
so I'm intending to export JSON for all my shape definitions... with the scalability built in
it seems like standard zome struts have a prism part at each end and an antiprism part in the middle?
I prefer "no-twist" struts that have a twisted frustum at both ends, and a clean, straight prism for most of the body
@Scott actually it seems to differ between zome generations
but you are correct, we could define a reasonably good algorithm for deriving a "nice" shape for an arbitrary orbit, based on ball geometry and some design principles
so what does the straight prism look like for a 3-fold or 5-fold axis?
it seems like you have the antiprisms for red/yellow in this vzome renderer demo thingy atleast
vZome's default style and "lifelike" style both use the standard shapes. Try the "tiny connectors" or "small dodecahedra" styles... under "System", "Shapes"
the latter two are no-twist prisms. I did make a style that uses normal balls and no-twist struts, but looks like I have not hooked it up. Probably unfinished.
From my latest "default strut geometry" class in vZome:
* Since every field allows octahedral symmetry, we want the richest triangular * tiling of that symmetry, which means we want the fundamental triangles of * the reflection group, by definition. Rather than rely on any symmetry orbits, * we can simply hard-code the vertices of a disdyakis dodecahedron, all * reflections and permutations of (8,0,0), (4,4,4), and (5,5,0). * (This is a simple approximation with integers.) * These vertices correspond to blue-like, yellow-like, and green-like axes, * in the usual Zome coloring.
What it doesn't say is how I derive a quadrilateral prism shape from that geometry... I'll have to read the code for the details
you can probably work something out easily enough, anyway
// y1n-----------g1n // |\ A /| // | \ / | // | y2---g2 | // |D | | B| // | | | | // | g2n---y2n | // | / \ | // |/ C \| // g1-----------y1
yes, I'm going to capture all my existing styles and put them in the base notebook... and add a parameter to select a style when rendering
I don't quite get what 'richest triangular tiling of that symmetry' means
meaning, how do you get the most triangles given a symmetry group... obviously you want the fundamental Coxeter domains (or their Gnomonic projection)
this is just for the purpose of drawing a rectangle pointing in some arbitrary direction?
An octahedron represents a (spherical) triangular tiling of the symmetry group, but it is "poor" in terms of the number of distinct triangles.
when vZome makes a new orbit (a white one), I need to render a strut
btw, would you consider changing your strut colors? they seem pretty arbitrary and not very well spaced apart
vZome allows rendering "styles" to use different colors, so I can define a new style with better colors
you can leave them. I don't really need to make models with arbitrary directions
I tried to make them well-spaced, but it got increasingly difficult... "maroon" and "cinnamon" are way too close, for example
did I link you to http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/ before?
it started with obvious things like orange (red-orthogonal) and purple (yellow-orthogonal). Then it proceeded to orbits that appeared in some 4D polytope projections
the vertex-first 120-cell brought in maroon, lavender, and olive
I want to make a little observable notebook which makes a categorical color scheme with widely separated colors
the face-first 120-cell brought in ... navy, peach, and sulfur? not sure
I think something like these quasirandom sequences can be used to choose colors within some other constraints
vZome used to try to generate quasi-random pastel colors, when generating new orbits
e.g. from a roughly cylindrical region in CIELAB space (or the like)
so it would try to pick colors which are more or less all pretty brightly colored
It is going to take a few days of work at least to figure out the particulars
colored orbits are very much at the heart of the aesthetic of vZome, so this would be very good
of course there would still be no meaningful association between directions -> colors in such a system
but could you "seed" it with 17 or so predefined colors? 😉
you might have to do some additional work on top for that
yes, a scheme that blends colors according to the RYB corners would be interesting
I have had various times where I needed to pick several separated colors
e.g. I picked like 8 or 9 distinct colors for an iphone game once
but I thought it would be too confusing... nearly red in color AND direction would be hard to distinguish from exactly red
David has done a bunch of hidden coloring tools for vZome, using all sorts of information from a model. I'll have to show you how to enable it.
I'm going to head for bed shortly... let's try to talk tomorrow evening?
I'm enjoying this immensely, even if I have trouble keeping up with you sometimes... hope it is working for you
one more explanation... the existing colored orbits correspond to "great circle" intersections. For example, draw a green circle wherever a green plane intersects the sphere. Most of the colored orbits fall on triple intersections, at least. All fall on at least a double intersection. I should do a notebook about these lines and planes.
yeah, drawing lines on a sphere is pretty easy with d3-geo stuff
you can easily project onto other map projections where some of the features are more visible
yes, that will be a fun one... can use your math notebook
I'm imagining something pretty interactive... select an orbit to add its planes, then generate new orbits from plane intersections... and capture the orbit for use in vZome etc.
@Scott I'm also curious if 3JS (or other existing webgl code) can make a more photorealistic render
without going all the way to using a ray tracer offline or whatever
there are various techniques for shadows and gimmicks like that... but I don't think anyone has a live ray-tracer yet
but rounding off some of the corners, adding some surface texture, using fancier lighting methods, etc.
one popular new idea is "ray-marching"... Henry Segerman and friends have been using that, also Roice Nelson
improved lighting is important. I've never been to excited about slowing down the rendering... I usually look for ways to render more objects
and you pretty quickly get into the realm of sending arrays to the graphics card
threejs does offer the ability to customize shaders, I believe. I have never played with it
I did do quite a bit with vertex buffers when I worked with Android, but that has been 4-5 years ago now
@David Hall you think this is a big enough cache? https://beta.observablehq.com/@jrus/zome-arithmetic#simplify3
@David Hall or should I also be adding a hash table cache to the GCD function itself? I doubt there is going to be all that much arithmetic with values ≥ 256, but that’s just my speculation
the nice thing about caching just small arithmetic is that the results can be 8-bit integers, so the cache doesn't take much memory
the main win here is probably avoiding trying the GCD for values that are already simplified to lowest terms. though this code still takes 27 arithmetic operations just to remove/add signs, compute the index into the cache, and so on.
vZome only calculates gcd for a pair of integers (actually longs). Its cache is a 128x128 byte array. Otherwise, it's calculated.
@jrus, take a look at https://beta.observablehq.com/@david-hall/gcd-algorithms
@David Hall that doesn't save you the division steps though
what I mean is, to simplify you need to (a) find the gcd, and then (b) divide each component by that vallue
I don't have it any more, but at the time, I ran a count of how often the gcd cache was hit vs calculated when opening all of the regression files and decided that 128 really was a reasonable trade-off. It only used 16K for the cache, which was hit a good bit of the time. I don't recall the number though.
The point of my original email wasn't necessarily to recommend a cache as it is to suggest the Google Commons gcd algorithm, though caching is easier to implement, especially if you think you'll be using a lot of small integers.
ah. I doubt a 50% performance difference only on large values makes that much difference for most purposes. Or if it does, adding another layer of hash-table based cache to the GCD function is probably a better fix.
it doesn't really hurt anything to switch to the binary gcd algorithm, though it's a bit trickier code
@David Hall did you test the relative performance not just for single GCD operations but for the full simplification of arbitrary values you typically get as the result of algebraic rational arithmetic?
It was only after I had written most of the email that I discovered JavaScript uses floating point numbers under the hood, so it is limited to Number.MAXSAFEINTEGER rather than Long.MAX_VALUE, and the best algorithm was based on a 64 bit integer, so it may not even work correctly in javaScript.
@David Hall one thing about the binary GCD algorithm is that it doesn't have much advantage vs. the Euclidean algorithm when the sizes of the two integers are very far apart (because then number of iterations and therefore cache misses increases, and this outweighs avoiding some divisions)
@David Hall so the advantage for a 3-element GCD is less than for a 2-element GCD on average
The only such test was to notice that gcd dropped way down from #1 in the CPU usage chart of the profiler. I didn't get more specific than that.
and the advantage really goes away if you have, say, 5 or 6 elements
one way you can improve things is to do one division step, followed by the binary method
this roughly normalizes the sizes of the two integers up front
[this is assuming you are trying to simplify a proportion of like 4 20-digit numbers or whatever]
really all this performance tuning depends a lot on what kind of values you are typically dealing with
I haven't looked at your code enough to understand the use case for gcd with more than 2 parameters since it seems they would eventually boil down to the 2 param function.
but if you have arbitrary numbers, there is a good chance that gcd(a, b) is already a very small number
right, so caaching the 2 param version is sufficient and way less overrhead in code and memory
but sure, it's plenty possible to only cache up to 127 if that covers most uses
the big questions are (a) how big are the values you typically need to simplify, and (b) how often do you get repeated attempts to simplify the same values vs. unrelated ones
I guess so, but I was raised on 64K ROMs in embedded controllers. 😉
the advantage of caching the 3-input simplification function is that you skip the division steps
avoiding 3 divisions is likely to be a big speedup if you need to simplify small arithmetic for a very large number of poitns
so that would be another thing to benchmark to see if the cache is worth the ROI
for vzome, or even for whatever we might want to do with golden rationals in the browser
It seems odd to me that 50M of RAM is considered cheap, but ??? division(s) are expensive. Yes. Ultimately, it comes down to finding where the real bottle necks exist and fixing them, not where we think they might be. That's why I said the java optimizations in vZome may not translate to javaScript.
well this would be under the assumption that you need to do millions of simplification steps
like, someone wants to make very complicated zome models with huge numbers of points by clicking on little buttons and you want instant response
50m is trivial in the sense that my browser routinely uses 20 gigabytes of memory
it's obviously not free, but individual web pages are often full of 1 megabyte each images, videos that take hundreds of megabytes, etc.
Another thing I was addressing in vZome at the same time as gcd was handing overflows. It was a trade-off between using native longs whenever possible and using BigIntegers when necessary, and transparently dealing with that in the BigRational class which is the basis for nearly all math in vZome. Again, that's probably been done in some well tested libraries which have their own gcd functions.
I was curious, how often do you do arithmetic in vzome that overflows 64-bit integers?
Most models don't overflow, and I was surprised to see that I could actually do a lot without needing a denominator greater than 1. In other words, lots of the coordinates are in the order of + or - 1023 and often less.
Another way to answer your question is that Scott has had vZome for maybe 15 years, and the overflow bugs just surfaced for the first time AFAIK, last year when I was messing with somewhat larger models in the HeptagonField. That's when I fixed them and tweaked the gcd method in the process.
Currently, vZome only specifies 3G total for the JRE to use at run-time.
@David Hall yes there is a huge amount which can be done with just integers with no need for rational arithmetic. if this can be assumed it saves a lot of computation
anyway, okay, your answer is encouraging that we won't need super large integers for most "build and display a zome model" kinds of use cases
Right... Only models that do successive generations of divisions would approach large denominators. I'm not sure why that happened in the heptagon-based models... It might not have been denominators at all, just "finer grained" heptagonal numbers... Bigger coordinate factors
Do a VEF export, @David Hall ... See what the numbers look like 😁
@David Hall okay, I added caching to both gcd and simplify, ~2 megabytes and ~6 megabytes, respectively, which take ~35 and ~50 milliseconds to compute at startup on my desktop machine
@David Hall additionally the gcd will return a cached value as soon as the size of the numbers gets small enough
I suspect there won't be much further benefit (beyond the caching) in using the binary algorithm, so I'm not going to bother implementing it
also I switched cache index order, so small denominators end up at the beginning of the cache and values with the same denominator end up nearby. The very start of the cache should be read much more than the rest, so this might reduce the number of cpu cache misses.
I'm not going to worry any further about GCD optimization unless there's some obvious bottleneck after profiling
@Scott when you make 3js faces for rectangles/pentagons, those get triangulated right? How do you decide which triangles to use?
I just make a fan based on the vertex order in the face
Works as long as the first 3 vertices are not collinear, or something like that. David recently removed that restriction on a branch, I believe... Moves the fan Apex as necessary
when you render zome balls, are the rectangles golden rectangles?
so to get from the center to an outer ball maybe takes some half blues or half greens?
I think I have the 'diameter' constructed via red-green-red
ah yeah, I figured it out, I can also construct it via 3 blue struts
I'll have to try your RYYR when I get home, since I don't have Mobile vZome yet
@Scott as soon as there's an easy vzome -> web pipeline I can make this kind of model and share a link that can be spun around etc. 🙂
@Scott eventually I'd like to get the code for generating the shape geometry for rendering models into a browser
then anyone interested can see directly how it works without going to 2 placex
@Scott so what's a good baseline strut to consider to be length '1'? half the shortest strut zometool sells?
personally I think it would be better for exposition if the middle length is considered '1', and then it is more obvious to readers/students that the size can also be reduced and the powers of phi can go negative
but that probably doesn't match convention as well w/r/t existing zometool stuff
did anyone ever 3d-print extra-short ("size 0"?) blue or yellow struts?
so okay, the radius of a large zomeball looks like either (φ²+1, φ³, 0), or (2φ², φ, 1)
@Scott oh actually if the distance between the centers of two touching balls is 2, then we actually get some fractional coordinates for the ball shape vertices. ends up as (1, ½φ⁻, ½φ⁻²)
You get to decide: nice coordinates for the ball and (most?) strut shapes, or nice coordinates for Zome models-as-vectors
if you want to avoid rationals, probably need to go with the former
oh, the ball itself should have half-integer coordinates for sure
trivial to accommodate scaling in the vZome export, or in the @jrus/** import
the centers of the ball should be some multiple of 2 apart along the blue axis etc.
also, you only need to do the math for drawing a ball once
the question I would have is whether the zometool short blue strut should be length 2, or whether the touching balls should be length 2
I don't really care about other ball shapes for the moment. but maybe you care about those if you are saving arbitrary vzome stuff
right. I wish (a little) that I had done the former... numbers would be less confusing to users who "show properties"
and I already display lengths in terms of "orbit unit"
I haven't removed that restriction yet, though we discussed doing so. The related PR was just some prelim groundwork related to getNormal() that shouldn't change any existing behaviors, but I did have that change in mind. I wasn't going to work on removing that restriction until after you pull in the prelim PR.
... that is the restriction of the first 3 vertices being collinear.
which BTW, doesn't apply to VEF import (or clipboard paste).
"prototype" is the canonical vector for the zone... but does not imply anything about strut lengths, it just defines direction
personally I think it makes sense to call that one the "0" strut, but that disagrees with the little numbers printed on the physical struts
in some cases it encodes a bit of history... I remember rescaling purple struts (the orbit unit) at some point around 2004
@Scott is there a canonical name for the different vertices, edges, faces of the zome ball? i.e. the different versions of each 'orbit'?
does it make sense to try to teach readers some numbering / naming scheme?
probably not, but that is said in the context of vZome, not a system where they see more of the code and coordinates
but again, I'm re-evaluating many of my long-held positions, in light of the different requirements of Observable
internally, vZome assigns indices 0-59, with some consistency (favoring certain rotations)
the old Zomod names are somewhat arbitrary, but my Zomic language adopted that as a convention
With Observable, I would happily create a notebook that explained either numbering scheme.
I just don't know if you want to base anything on it. It seems you're kind of stuck with something, if you want to let people "build" with an API
Remember your comment about "turtle graphics in SVG"? I've had thoughts about orientation relative directions in an API. I just don't think it would be very easy without a visual aid constantly in front of you. I built a couple of different "keys" for Zomod/Zomic to make the scripting even possible.
you can also just tell people some quaternion coordinates
That sound like fun... a quaternion based zomic turtle API 😉
but how to you remember orientation? Easy for a computer, hard for a person unless they have the model building in front of their eyes
but by that time, you might as well use a direct-manipulation UI and simply record a script
Maybe a flight simulator paradigm... fly the turtle around and optionally leave a strut in your wake.
@Scott but anyway, leaving aside quaternions for the moment, it's pretty convenient now to have the code written that I can say:
${deduplicate(icosahedral_symmetry([[1,0,1], [-1,1,2], [2,-1,2]])).map(vprint)}
to get all of the vertices of the zomeball
and even more, looking forward to the next step, when you want to build two connected struts then apply symmetry
I know. I was just messing with y'all. I'd prefer quaternions, if for no other reason than to avoid gimbal lock
I'm going to keep quiet, @jrus, and see what you come up with as a symbolic representation of strut building. Fortunately, we can always fall back on vector addition... but designing (and naming) vectors is the hard part
@Scott my plan was to just directly specify coordinates of endpoint vertices 🙂
we could pretty easily translate from whatever existing system
red names are reasonable, but I never did memorize the relationship between red, yellow, and blue names
they're just separately numbered in rings starting at red poles
oh, right... the red rotation is the core principle. I did a little better in my icosahedral orbit numbers, just so I could code them
there should be another PDF for that key, but I'm not sure where
for example, can number the octants, and then each red strut is at the intersection between two octants
each yellow strut is either in 2 octants or is only in one
I wouldn't mind your reinventing vZome, if you weren't doing it better! 😉
I wanted consecutive indices... I don't number strut orbits, I number the orbits of the symmetry group.
yellow 9, 10, 11 all have coordinates (1,1,1) (up to scale)
if you have 60 symmetries then your fundamental shape is a kite
for non-chiral orbits, you simply use a sign bit (even for zero), and it means "negate all coordinates"
for chiral orbits, that works when the symmetry group supports the triple reflection (central inversion),
you could also just negate one coordinate, and draw a triangle under gnomonic projection that is double your picture in vzome
one that I would be interested to see how you'd deal with is the 24-cell
https://en.wikipedia.org/wiki/24-cell
we can generate any member of its family of Wythoff symbols
Brian Hall gave me a recipe in terms of a root system and quaternions... I use it for B4/C4/D4/F4 groups
I find it very interesting as 4-dimensional shape. the 4-d shapes with 5-fold symmetries are even more too much for my brain
the 24 cell is what you get if you make a voronoi diagram out of hurwitz quaternions
where "hurwitz quaternion" just means you have either all integer or all half-integer coordinates
so you just take 2 copies of an integer lattice, and offset one in every dimension
4D polytope generation is another thing that would fit better in an Observable notebook than it does in vZome, in a sense
I think there are a lot of mysteries in these 4-d shapes that are not very well understood, either formally or intuitively
but I admit I am far from an expert, and there are certainly some mathematicians who know a lot about them
I would agree. They also attract crackpots... those "mysteries" include brain function, crystal power, etc...
make a notebook about the 24-cell, and you'll get some of those people drawn in 😉
It might be interesting to manipulate 4D in the UI, using the trackball for 3d and a wheel (on screen or the mouse wheel) for rotating the 4th dimension
one control I'm really missing: drag the 4D vertex (or vertices!) to be used in generating a 4D polytope
moving a point around on the 3-sphere, within a hyper-spherical triangular reflection domain, while snapping to golden number coordinates
the equivalent on the 2-sphere would be the place to start, of course
Yeah, I wasn't thinking about editing 4D, just viewing.
oh you folks might enjoy the latest 3blue1brown video (not public yet)
yes, even viewing is tricky if you want to snap to golden vectors
I have intended to make some related(ish) interactive demos explaining quaternion rotation for a while
Cool. I heard him mention that this is coming soon...
I really envy his productivity and creativity... great stuff
anyway, trying to make a usable visual for 3d rotations (ideally practically useful when rotating models, or if making a 3d turtle graphics thingy or whatever) is a neat problem
a stereographic projection of quaternions onto space (and then showing points within a ball) is one nice one
another view is to show a little arrow pointing along a great circle arc, and slowly spinning around the sphere
what I want to explain is how quaternions compose, in some kind of visual/intuitive way
anyway, we're going to go walk the dog around the block etc. might be out for an hour or something
I'm going to watch the video then go to bed myself.
I'm struggling with the three.js import... can you help?
I'm just missing controls.update() inside a render function... but Mike's example for OrbitControls doesn't seem to need a render function calling requestAnimationFrame, and his basic Three.js demo just has a while loop for animating
I'm wasting time reading documentation that I don't need to read right now. I might just go read a book or something
requestAnimationFrame is global, so that mechanism won't work... I need to read the notebook about timers etc.
you can use requestanimationframe if you want, but it's not the most idiomatic
OK... well it is not working, so I'll try the more native idioms
yup, read it... now wondering how to fit it into my function
It only works when the domElement is allowed to default in the trackball. I'll debug further tomorrow. The code seems quite a bit too complicated for what it is doing.
I ended up with requestAnimationFrame... I didn't see how to make 'yield' work
I've been re-reading about promises, generators, etc.
now I have a work escalation... our big user conf is next week, and everyone is making demos and finding problems, of course
wait, what happened to the chat history. all I see is gray boxes, the word 'today', and my comment
there's a lot of stuff to learn; I don't really feel competent in that part of JS
oh, we have a free account here, so there is loss... but it should not be asymmetric
I've kept my browser tab open though... if you stopped the app, it may be lost for you
somehow it decided that it needed to reload the content from the server or something
in the mean time it hid all the text that was previously there and replaced it with gray boxes
it's not entirely obvious what the right way is to construct a "lifelike" zome ball. here's one alternate idea
The model used in vZome. This is an import of a VEF export, so you cannot "undo" and see how it was built, but you can hide panels to understand the geometry.
@jrus to understand how to drag in a cell, I cloned MB's notebook: https://beta.observablehq.com/d/e1e6bd2c546879ce
I don't think the dimensions are quite identical, but yours might better match zome geometry
then again, I am just visually inspecting, not measuring. hard to be sure.
@Scott when you make shapes for physical 3D printing, how much do you have to tweak the geometry of the connectors?
@Scott not sure if I linked this precise page http://www.rawbw.com/~davidm/zome/
the real chamfers are rounds, I think, with the modern ball mold
My 3D-printed struts use a standard size basis given by Paul... .700 in plus some shrinkage, maybe .698 in... as the ball blue diameter, if I recall correctly
OK, gotta bounce back to the work account... now two issues in flight
btw, one other naming scheme for orbits could do a gnomonic projection onto the perpendicular-to-red plane, and then use natural number coordinates representing displacements along the 5 5th roots of unity
instead of doing a gnomonic projection onto a perpendicular-to-blue plane
oh wow, Simon is able to put some zometool shapes together
I was expecting him to not really have enough hand eye coordination for another 6 months at least
I guess it is like a much smaller version of the baby toys... Square block in square hole etc
@Scott we haven't tried other colors of struts yet. we'll see if he can figure out the green ones before 3
we've done some playing with tinker toys. I think the blue zometool struts might be easier than those, just based on how much strength it takes to push them in
@Scott so if you have 'copy' implemented, are you going to also add 'paste'?
there is already paste, but only for VEF format (which is what you get with copy)
you have to have a ball selected to paste, as the "relative origin"
I'm nearly ready with a JSON dump of any "rendering style" in vZome. I already have the JSON for any symmetry, any field... orientations, permutations, orbits.
well, getting there... I'm having a little trouble controlling the JSON serialization
the annotations and APIs are rich and not well-documented
This is the default style from vZome. When I first defined the JSON format, I decided I didn't want to make integer coordinates complicated by the possibility rational coordinates... this was back in May, working with my friend doing AR
Observable question: when I import from another notebook, I get local instantiations of any cells I transitively depend on, right?
Perhaps not. It talks about "live bindings" : https://beta.observablehq.com/@mbostock/introduction-to-imports
No, I don't think he is trying to say that imports share state across notebooks.
@Scott any cells in the dependency graph will get used, but you shouldn't see their names unless you ask for them, as far as I understand
@Scott what my code was doing was just taking an optional third parameter for the denominator
so if you want just an integer you can write it as [1], if you want a 'golden integer' you can write φ as [0, 1], or if you want a rational number you can write it (2 + φ)/3 as [2, 1, 3]
and for a field of order N, the rational form can always use N+1 integers
whatever you think is expressive enough, reasonably legible, reasonably concise, etc.
I prefer not parsing, so the format is not limited golden, or limited to order 2 fields
it's not hard to add another format for some other field, etc.
again, my focus is to make sure I'm never too far from rendering any vZome file
if you want other fields maybe you want the denominator listed up front
as long as I know the order, it is not necessary, I think
I can now export JSON for shapes, either with floats or with algebraic numbers
yes, I like that... originally I did that too, but I don't remember why I backed away
and bigrational doesn't support proportions of 3 integers?
how does the bigrational performance compare to just using bigintegers in your own "golden rational" type?
maybe this one makes it simpler if you let the existing code handle division etc.
handling inverting of a golden rational number is still a pain
so I've dropped the Java work for a bit, to focus on how I'll deal with these shape-sets in the Javascript
it's no more than a fes lines of code to handle any of these formats
I asked about imports because I think any "renderer" can have its own cache of geometries, only creating those needed to render a single model
I don't really understand what the question is. you can put the "cache of geometries" in the same notebook as the "renderer"
The issue is one of open-endedness... each different length of a given orbit needs its own Geometry in three.js terms. If I precompute some fixed set, it will necessarily leave out an infinite number of possibilities, like a strut 2/3 in length, etc.
So some models will always need to dynamically generate Geometry objects from a recipe, either generic or per orbit
vZome handles this as I mentioned before, by having a shape "recipe". This is what I'm exporting.
sure, but they need to be resolved by the time I give them to three.js
I can help you with that if you want. though maybe you want to make a separate library instead of using my little hacky-prototypes-in-a-notebook code
Here is the vZome StrutGeometry for the orange icosahedral orbit, for example.
if you do it without the floats you will save space, make it more comprehensible, and not add perceptibly much computation time to your notebook
and I don't get to control precision of the floats, with the JSON library I'm using, which is annoying
you mean it is more precise than you need, wasting bandwidth?
as you said, who needs the least-significant 10 digits?
you can also if you want make each of your fractions into two numbers in the list
yes, I could... but I like the one-denominator approach, so I'll figure that out
what I like about the one-denominator version is it makes it clear how big you need to make an integer-coordinate version
I suppose Javascript and JSON would allow wacky things like [ 2, 5, { "divisor" : 3 } ]
also I found most of the time the denominator was the same
I was also trying to toggle between triangulated faces and just polygons, but I think I prefer just polygons
easy to make code smart on that one. Only the naive default Geometry requires triangular faces... as soon as you do BufferedGeometry you have more freedom
getting organized in a new fork: https://beta.observablehq.com/d/b1cf328eed466d17
see this function: https://beta.observablehq.com/d/b1cf328eed466d17#createGeometries
I need one that operates on a single shape like the JSON I just sent you
assume rationals are in one-divisor form, like yours
here's the thing: the shape is given for only the prototype vector, but it has to "stretch" for other vectors in the orbit
logically, the createGeometry() function would take a length rational, but that means lots of division
better would be to align the target vector with the prototype, then just offset the "full-scale-vertices" to the end
gotta run to drop Lia at practice... more details later!
@Scott don't worry about division. it's not really any slower than any other arithmetic in my browser code
since the slow part in any case is the "simplify" that needs to do a memory lookup (or for bigger numbers, find the gcd and do 3 integer divisions)
@Scott for the geometries with an antiprism twist do you just keep those uniform in length? make them some proportion of the total distance? (what do you do if e.g. the strut is shorter than the antiprism part)
fullScaleVertices are the ones that should "telescope" out to the end of any strut
with the point just being that this takes not much code, so don't worry about it either way
halfScaleVertices are the (fixed size) antiprism that slides to the middle
what if there's not enough space for the halfscalevertices?
nothing will fall over, it just looks ugly. I stopped worrying about it a long time ago
actually the antiprism won't be inside-out... you can make vZome do this easily, I think
also converting to floats is easy https://beta.observablehq.com/d/db3ac0477ddb8fd3
I'm parsing parsegoldenrational... my Javascript reading is still subpar
@Scott here's better https://beta.observablehq.com/d/db3ac0477ddb8fd3
I haven't seen the tuple assignment used in that way, but I like it
this is not super robust; it's going to break if you pass it "5" instead of "5/1" or whatever
that's what I produce... I check "isInteger" to determine which to emit
I'm just saying, if humans tried to produce these files they could break it easily
needs to be (!(typeof a1 === "number")) instead of (!typeof a1 === "number")
So I didn't say it earlier, but my goal here is to render the VSON format... so for any strut we're rendering, we already know its orbit and zone and length
so we just need a Geometry for that length and orbit, then we'll use the quaternion for that zone when we add the Mesh
we can later make it work without the orbit, zone, and length, and compute them all as you have been saying
so I think it reduces arithmetic to use a matrix vs. a quaternion for actually multiplying vectors
sure, but then we need 60 different Geometries... if we let three.js do the final rotation, we need only one
you can see the pattern in my existing vZomeRenderer
we are replacing the "getGeometry(...)" bit, as well as changing the "strut" object
@Scott yes you should just save one geometry. I am just saying that to rotate it into the right place you might want to use a matrix rather than a quaternion
but maybe you don't need to do too many of those rotations and it doesn't matter too much
I wouldn't care... you can probably do either with 3js
I'm going to focus on refactoring my renderer with this model in mind
let me get some of the orbit and shape objects uploaded to gists
that's where each of the symmetries sends the other ones?
you can work just with "orientation" indices, and permute those
(until you actually need to reorient a vector, of course)
I don't use the permutations much any more, but they do come in handy for a few things
"If I rotate around red 14, where does it take yellow 23?"
yes, the first three are the permutations on the axes
you'll find 12 like that, of course... easily correlated by looking at the matrices
It may even be true that my Zomic language didn't use vectors at all... the orientation state of the virtual machine was just a permutation.
FYI, before I ever had direct manipulation with the mouse, I had an app that was driven by a Zomic script, and just rendered the result.
I should render out some gnomonic projections with numbered triangles
yes, something... I don't have a pre-formatted "key" for the orientations... just the handwritten numbered dodec you saw
this would be a great addition to my vZome Utilities notebook
it wasn't just numbered randomly and then each one manually computed 🙂
not as algorithmic as it could have been... I wanted to be able to read it
hmmm... feels like it is still missing some information
right... the createInitialPermutations function is specific to particular symmetries, but this part is not
forgive the ancient verbose Java... I'm sure even Java 8 would make this a lot simpler
okay no-stringed matrices https://beta.observablehq.com/d/db3ac0477ddb8fd3
cool... what's the diff between matrices and matrices2?
so okay, your 4 blocks are +++, +--, --+, -+-, then in each block that there's rotation around the red axis, then in each of those blocks there's a permutation of axes
that's pretty well the same as my icosahedron symmetry code but maybe in a different order
couldn't be too different, unless I did some inefficiency
I found the code shorter to put the yellow and blue loops together and unroll them
yes, but it could be done in terms of lists of permutations or matrices if you wanted
to get matrices out you can just dump [1,0,0], [0,1,0], [0,0,1] into that code
yes, that's similar to how I generate my matrices, IIRC
my code also started uglier than that. I just spent more time than I needed rewriting it until I couldn't figure out how to reduce it further 🙂
it's not actually quite as efficient as it could be; it does one matrix multiplication more than necessary
we can align later, or now, as you like. Since we have the matrices to go with vZome's indices, we are not blocked.
so how do you decide which orientation a given strut is in?
whichever index it ends up on, after my clumsy find-the-zone search
I don't care if there is a canonical name for red 23... at least not up until this point
it needs a bunch of tests w/r/t various planes through the origin
yes... let's not get into that again just now... needs face-and-whiteboard-and-screen time I think
one thing you can do is find the normalized vector defining the "orbit", then find the quaternion which rotates your original strut onto that one, and then just lookup that quaternion
we could write a whole notebook on different approaches... a fun exercise
so okay, given a vson file, what do we need to do to convert to a 3js-compatible format?
we have both vector and length/orbit/orientation we can work with
I'm writing the function that will call getGeometry and getQuaternion
also available as : https://beta.observablehq.com/d/b1cf328eed466d17#test_VSON_model
(I don't need to re-share after every save, right? The URL is fixed?)
but to get updated content you need to click the "reshare" link
"modified... reshare" warning and link never goes away
I should figure out if all the necessary quaternions can be stored using algebraic rationals
I think maybe so if we don't force them to be unit length
you mean using algebraic integers, avoiding rationals?
but I think for unit length some might have square roots?
well e.g. a 1/3 turn rotation about (1,1,1) should be cos(π/3) + sin(π/3)i + sin(π/3)j + sin(π/3)k
since a fifth turn rotation about the x axis is also golden rational
so we should be able to write a function for multiplying quaternions, and then build up the list of quaternions for these rotations using code pretty similar to the one I had posted for finding the symmetries of vectors
@Scott so okay, I'm going to stop fiddling with quaternions for the moment. what's your link?
working on a bug right now... everything is stubbed out so I can render balls
forgot that parseGoldenRational was not evaluating to float
feel free to make a function that directly spits out a float
so is this thing just ticking away irrespective of mouse input?
it seems to eat all my scrollwheel events from anything I do on the page
the problem is that the mouselistener is attached at the doc element
I could not get the scroll or drag to work on any other element
because you definitely want different behavior 'in production'
but I need help there, too... I copied the Trackball code in here just for debugging, but I haven't figured it out
ok, reshared... you should see hypershort yellow struts in different colors
and it just happens to illustrate the problem with the midpoint antiprism
I wonder how it would work if you just make the antiprism 1/3 the length of the part of the strut visible between balls
Maybe stuff gets tricky with very short struts of other orbits
not with an antiprism but with some kind of correction at the ends
really short struts get ugly regardless, even with good modeling, since they often just join the balls without reflecting the centerline (except for RYB)
sorry, poor choice of words... "without illustrating the centerline"
ok, balls with triangular faces are done... one triangle per face
now switched to "lifelike"... though it is not very, with only the one triangle per face
triangulating a polygon is a nice way to learn reduce
luckily you just do each object once and cache them, right?
OK, I used the same code for balls and struts. Without the adjustment for fullScaleVertices and halfScaleVertices, the struts are rendered inside-out, and inside the balls.
@Scott I don't think you can unpublish. only fork and delete
I tried to ignore strut length, and just use the shape prototype vector as the endpoint, and derive midpoint from that. It is buggy. going to bed
@Scott okay, I fixed two silly bugs in my caching gcd code. as soon as I publish your use of simplify3 won't be compromised. 🙂
first bug: I was using a cache of 8-bit integers for values up to 1024
second bug: I wasn't properly dealing with the case where the gcd itself is bigger than the cache size
it's quite possible my code had been breaking you if you were doing large arithmetic
and also to start with simple obviously working code before worrying about optimizations
Not sure... My notebook did not load when I tried. Didn't have time to wait and retry
I'm getting myself super confused trying to figure out the right quaternion for a 5th turn about the red axis
quadrance = 1/4(φ² + φ⁻² + 1) = 1/4(1 + φ + 2 – φ + 1) = 4/4 = 1
φ² + φ⁻² = 3 is the same fact that makes the yellow directions of (1, 1, 1) and (φ, 0, φ⁻) have the same length
I tried bypassing simplify3 with a variant that doesn't cache, but that didn't help... though who knows if reload might have fixed that
I might have accidentally published one with a syntax error or something
@Scott if you go to https://beta.observablehq.com/@jrus/zome-arithmetic does the grdiv cell have errors in evaluating?
no, I've been looking at your notebook all afternoon
we want combinations of [φ/2, φ⁻/2, 0, 1/2] to powers of 0–4, [1/2, 1/2, 1/2, 1/2] in powers of 0–2, [0, 1, 0, 0], and [0, 0, 1, 0]
is that right-multiplication, left-multiplication, or both (with a conjugate)?
you'll get the symmetries in a different order depending on which way you do it
ok... and the vector is just augmented with a 1 in the 4th coordinate?
as long as we have a convention, and can align the effects with the existing vZome matrices
it seemed like the 3JS version had named x, y, z, w, so the order doesn't really matter
I'm also not sure if you care about normalizing quaternions to have positive scalar component [the 3D rotation is the same for quaternions pointed in opposite directions, because of the sandwich multiplication]
you can think of there being either e.g. a rotation by 3π/2 anticlockwise or π/2 clockwise, which get separate quaternions
I'm OK with anything as long as the results match... if we try a model that has all greens (or better, some chiral strut), we'll have a good test
we can certainly make the code match vzome numbered directions
for that matter, I don't think I've done a verification like that even with my React code, which is the basis for my new renderer code
I'll have to go look at those again to figure out what that order is 🙂
OK, I've shared a new fork: https://beta.observablehq.com/d/70a3c92c576398bb
it has resolution errors, since it does not have grdiv imported
if you add grdiv to the import, I think you'll see some of the cells get wedged... no errors, just blank
I'm going to give it a few minutes... maybe it is just my poor little CPU
or maybe you are using grdiv in a way that causes the hang
could be... supposed to always be the latest publish
endpoint = shape .prototypeVector; midpoint = endpoint .map( x => grdiv( x, [2] ) );
prototypeVector has not been parsed to a one-divisor form
well, I chose a prototypeVector at random... I was guessing that any vector with 2-element coordinates would fail
I think I'm going to leave this code without typechecks in every function; part of the goal is to show how short each function can be if someone looks at this notebook
funny, not finding any strings in the prototypeVectors
hmmm... still something wrong with createShapeGeometry
so yeah, parsing those to 3-element rational representation might fix the problem
OK, fixed that ... much better now. I still have a defect, but not so catastrophic. Reshared.
just need to break up createShapeGeometry into smaller, testable functions
silly defect fixed... render giving me red and yellow struts now, though not scaled right (and obviously not rotated right)
```icosahedralquaternions = { const cartesianproduct = (u, v) => [].concat(...u.map((a) => v.map((b) => quatmul(a, b))));
const one = [[1],[0],[0],[0]]; const powers = function powers (q, n) { for (var i = 2, array = [one, q]; i < n; array[i] = quatmul(array[i-1], q), i++) {}; return array; } const red = powers([[0,1,2], [-1,1,2], [0], [1,0,2]], 5); const yellow = powers([[1,0,2],[1,0,2],[1,0,2],[1,0,2]], 3) const blue = [one, [[0],[1],[0],[0]], [[0],[0],[1],[0]], [[0],[0],[0],[1]]];
return html${cartesian_product(red, cartesian_product(yellow, blue)).map(vprint)};
}```
oh the last line would not in practice return html, but just the list of quaternions
maybe better to just be more explicit, do the loop for red, and write down the yellow vectors directly
```icosahedralquaternions = { const cartesianproduct = (u, v) => [].concat(...u.map((a) => v.map((b) => quatmul(a, b))));
const one = [[1],[0],[0],[0]]; const red = [one, [[0,1,2], [-1,1,2], [0], [1,0,2]]] for (var i = 2; i < 5; red[i] = quatmul(red[i-1], red[1]), i++) {}; const yellow = [one, [[1,0,2],[1,0,2],[1,0,2],[1,0,2]], [[-1,0,2],[1,0,2],[1,0,2],[1,0,2]]]; const blue = [one, [[0],[1],[0],[0]], [[0],[0],[1],[0]], [[0],[0],[0],[1]]]; return cartesianproduct(red, cartesianproduct(yellow, blue)); }```
I don't think either would be too confusing if I were a bit more used to ES6 and functional programming
```icosahedralquaternions = { const cartesianproduct = (u, v) => [].concat(...u.map((a) => v.map((b) => quatmul(a, b))));
const one = [[1],[0],[0],[0]]; const red = [one, [[0,1,2], [-1,1,2], [0], [1,0,2]]] for (var i = 2; i < 5; red[i] = quatmul(red[i-1], red[1]), i++) {}; const yellow = [one, [[1,0,2],[1,0,2],[1,0,2],[1,0,2]], [[-1,0,2],[1,0,2],[1,0,2],[1,0,2]]]; const blue = [one, [[0],[1],[0],[0]], [[0],[0],[1],[0]], [[0],[0],[0],[1]]]; return [red, yellow, blue].reduce(cartesian_product); }```
```icosahedralquaternions = { const cartesianproduct = (u, v) => [].concat(...u.map((a) => v.map((b) => quatmul(a, b))));
const one = [[1],[0],[0],[0]]; const red = [one, [[0,1,2], [-1,1,2], [0], [1,0,2]]] for (var i = 2; i < 5; i++) red[i] = quatmul(red[i-1], red[1]); const yellow = [one, [[1,0,2],[1,0,2],[1,0,2],[1,0,2]], [[-1,0,2],[1,0,2],[1,0,2],[1,0,2]]]; const blue = [one, [[0],[1],[0],[0]], [[0],[0],[1],[0]], [[0],[0],[0],[1]]]; return [red, yellow, blue].reduce(cartesian_product); }```
function triangulate( polygon ) { return polygon .reduce( (tris, vertex, index, array) => { if ( index >= 2 ) { tris .push( new THREE.Face3( array[0], array[index-1], array[index] ) ); } return tris }, [] ); }
https://gist.githubusercontent.com/vorth/b6294348ebfb7846d7cbe29f1ec38b9f
crap, I was doing the matrix=>quaternion conversion on the Java side, using Java3d
oh, I started doing some unreleated code cleanups. so my time estimate is not very useful
I'll reshare... my notebook now has the icosahedral matrices in useful form (yours)
bah, sorry. didn't actually get to spitting out matrices. did clean up some other code.
not sure... I would think the former is more conventional
reshared some cleanup... removed all the old float-based renderer stuff
oh, it also matters what the index convention is for matrices
as long as I've created a Matrix4 with the right indexing
@Scott anyway, okay, I spent the extra 5 minutes to finally write this code, after an hour and a half reading picture books
quat2matrix = function quat2matrix(q) {
const [m00, m10, m20] = quatmul(q, quatmul([[0],[1],[0],[0]], quatconj(q))).slice(1);
const [m01, m11, m21] = quatmul(q, quatmul([[0],[0],[1],[0]], quatconj(q))).slice(1);
const [m02, m12, m22] = quatmul(q, quatmul([[0],[0],[0],[1]], quatconj(q))).slice(1);
return [
m00, m01, m02,
m10, m11, m12,
m20, m21, m22]
}
yes, conversion in that direction is pretty straightforward
I'm not exactly sure what the accepted best practice is to convert any arbitrary matrix to the "closest" quaternion
@Scott just to be clear, what order are your matrices written in?
I'm pretty sure each "matrix" is a column of row vectors
and I multiply using the matrix on the left and a column vector on the right
so your single red rotation is for me 4 red rotations and a yellow rotation 🙂
@Scott oh actually, bleh. you maybe didn't order it that way
so I think to imitate your version I need to just explicitly paste in 5 red quats
actually the story is that your yellow rotations aren't all the same, but depend on which red rotation we are in. maybe I am just multiplying in the wrong order. let me test that.
@Scott okay I think that works, maybe you want to check. let me publish
looking, but I'm having trouble comparing visually with the vprinted output... let me get mine in that format
@Scott side by side https://beta.observablehq.com/@jrus/zome-arithmetic#quat2matrix
@Scott I think you multiply these things in a different order than the way you order them. so I can't just use a single cartesian_product function
it's only a couple lines of code to handle. not really a big deal
for me, the problem would be figuring out which couple of lines of code!
```icosahedralquaternionsscott = {
const one = [[1],[0],[0],[0]];
const red = [one, [[0,-1,2],[-1,0,2],[1,-1,2],[0,0,1]]];
for (var i = 2; i < 5; i++) red[i] = quatmul(red[i-1], red[1]);
const yellow = [one, [[1,0,2],[1,0,2],[1,0,2],[1,0,2]], [[-1,0,2],[1,0,2],[1,0,2],[1,0,2]]];
const blue = [one, [[0],[1],[0],[0]], [[0],[0],[0],[1]], [[0],[0],[1],[0]]];
const output = [] for (let b = 0; b < 4; b++) { for (let r = 0; r < 5; r++) { for (let y = 0; y < 3; y++) { output.push(quatmul(blue[b], quatmul(yellow[y], red[r]))) } } } return output; }```
there is a logic to it, just not something I can express as math... we'd have to look at the dodec key together
also the ordering of multiplications B * Y * R is different than the order of loops, B, R, Y
I need "icosahedralquaternionsscott.map(quat2matrix)"
At some point I should figure out which red strut you are rotating around
I think of it as the opposite, but we are probably rotating in the opposite sense
https://beta.observablehq.com/d/70a3c92c576398bb#icosahedral_quaternions
if I want "new Quaternion(x,y,z,w)" and I have list=[x,y,z,w], can I do "new Quaternion(...list)" ?
@Scott oh good point, we could flip all the signs on that quaternion and get the same thing
it was just negative based on the ordering from my other rotations
reshared... we have some orientations, just not quite the right ones 😉
and then at some point down the line making sure it doesn't nail the CPU
definitely needs to stop doing that until there is actually mouse interaction 🙂
maybe I can make a nicer LaTeX print style for matrices here, including pulling out common denominators to put up front
@Scott this is fun https://beta.observablehq.com/d/6ac0fb0c3dd0958d
okay, I spent the last 15 minutes skimming through random recent observable notebooks; time to sleep. I think we might get best bang-for-the-buck implementing something with d3-drag for mouse interaction
Sounds good. We can have it rotate the object group, rather than the camera, and trigger a render
3js.OrbitController is certainly smarter about mouse listener attachment, and may be rendering only on change. Just uses the wrong math
@Scott for of loops are way better ```icosahedralquaternionsvzome = { const o = [0], h = [1,0,2], one = [[1],o,o,o]; const red = [one, [[0,1,2],[1,0,2],[-1,1,2],o]]; for (var i = 2; i < 5; i++) red[i] = quatmul(red[i-1], red[1]); const yellow = [one, [h,h,h,h], [[-1,0,2],h,h,h]]; const blue = [one, [o,[1],o,o], [o,o,o,[1]], [o,o,[1],o]];
const output = []; let b, r, y; for (b of blue) for (r of red) for (y of yellow) output.push(quatmul(b, quatmul(y, r))); return output; }```
I should stop fiddling with these
icosahedral_quaternions_vzome = {
const
one = [[1],,,], h = [1,,2],
blue = [one, [,[1],,], [,,[1],], [,,,[1]]],
yellow = [one, [h, h, h, h], [[-1,,2], h, h, h]],
red = [one, [[,1,2], h, [-1,1,2],]];
for (let i = 2; i < 5; i++) red[i] = quatmul(red[i-1], red[1]);
const output = []; let b, r, y;
for (b of blue) for (r of red) for (y of yellow)
output.push(quatmul(b, quatmul(y, r)));
return output;
}
ROFL. That's fun... You'll have to explain how it works later
So zero is the default for any missing array element?
Ok. The language must define some meaning for just commas, though, right?
oh maybe I should be careful. It seems that array.map() just skips over undefined stuff
irrespective of what the map is supposed to do with the values
apparently if I get the .values() that explicitly includes 'undefined' entries, instead of just being empty
@Scott okay, I made the matrices print prettier https://beta.observablehq.com/@jrus/zome-arithmetic#quat2matrix
anyway, enough of this nonsense. I should figure out something useful to do
feel free to do anything you like... there are lots of refinements we can make to the 3JS code, even just syntactic ones
okay. maybe I should go work on paper for a bit. I don't have an immediate goal
think about bridging the gap between your segment set and something analogous to VSON... orbit, orientation, length all resolved
start with what we can do easily... we can render struts in the wrong direction, better than nothing
you have an orbit finder, largely... length is just division
divide, and then either (a) figure out how to square root
I mean, we can get the relative quadrances between the prototype and the actual struts
let's start with just orbits... render struts the wrong length
so what's the best algorithm for taking the square root of a known-perfect-square algebraic rational?
I don't think you need to go there... I believe vZome does it by getting the vectors parallel, then comparing some nonzero coordinate of each
guaranteed that one of the three coordinates will be nonzero
it is a bit of a relief when you make the occasional error... like you're trying to pass a Turing test 😉
Once we have segment-set => VSON, we can start going crazy with all sorts of functions to generate objects from code
it is basically the guts of vZome... with API instead of pre-built commands
and also an ability to display several models serially down a page
we can do that now, but they'll all spin with the single trackball!
I suspect that webgl can update just the camera without doing that much work
you could try switching back to 3js orbitcontrols... I don't think it insists on the animate loop
it would give us a framework to build on, get the mouse listeners and generators right, then swap out the math
I want to at some point play around more with d3-drag for mouse stuff
it's all good... whatever keeps you energized is fine with me
mouse handling is always the hardest part of any UI work I have tried to do
not the basic logic of the mouse handling, but just figuring out data flow, getting the edge cases to not break, etc.
I am still not sure what to do when I want to e.g. put a bunch of selectable stuff on screen
for instance, if I wanted to make an editable bezier spline tool, like Illustrator or whatever
where you click a path and see all the knots. then click a knot to see extra handles. with the ability to drag the whole path, or just drag one knot, or a selected group of knots, ...
yeah... one reason I feel good about React+Redux. It really simplifies how to think about that
Though it is a bit slanted toward typical vanilla data (rather than cool mathematical objects), I really liked this: https://reactjs.org/docs/thinking-in-react.html
I have not yet figured out how to meld React and Observable, conceptually. Shouldn't be too hard
wha?... my rendering of 60 spruce struts worked last night, but tonight they are not connecting up. Did you reorder in the process of playing today?
or check the side by side https://beta.observablehq.com/@jrus/zome-arithmetic#scott_matrices
I made a fork and disabled the trackball/animation loop... CPU idles normally. There is nothing inherently abusive about 3JS, so we can certainly make it well-behaved, even for multiple canvases
The TrackballControls class seems to require this sort of behavior:
Clearly evil... render each tick, regardless of whether there was actually any update
I'll switch back to OrbitControls. I imagine we'd rather have a clunky interface that does not peg the CPU
OrbitControls is just better-written, I think... they are all contributed examples
could be that mrdoob himself wrote that one, or someone equally adept
Your bug was actually fun... I knew instantly what had happened (though not in precise terms).
half the zones would have been swapped (one quarter with the other quarter); instead of doing one 180° rotation, you get a different one
doesn't take much broken symmetry for your eye to see "no symmetry!"
controls.addEventListener("change", () => renderer.render(scene, camera));
@Scott one nice thing about d3-drag is that it will keep working as the mouse moves outside the page, etc.
this one is definitely better than the other mouse control, even if it has a stupid north pole always up feature
I'm not sure whether it is better for mouse actions to depend on initial mouse position or only relative movement
drag outside the canvas or page is definitely a plus
ha, I knew it. OrbitControls authored by mrdoob and alteredqualia... rock stars
OK, the CPU peg was my stupidity, not anyone else's. Refreshed... check it out.
Still cannot get it to rotate and pan on the canvas only, sorry.
I realized that I'm not approaching this correctly. The renderer code itself is doing the conversion to one-divisor rationals. That means we can't feed it one-divisor rationals to start with.
so I'm going to just define a function that "cleans up" a VSON model, normalizing the numbers to one-divisor
so the renderer can just assume the normalized format
@Scott you might also just want to directly put the list of 60 quaternions in your notebook 🙂
quaternions = [
[[1,0,1],[0,0,1],[0,0,1],[0,0,1]],
[[1,0,2],[1,0,2],[1,0,2],[1,0,2]],
[[-1,0,2],[1,0,2],[1,0,2],[1,0,2]],
[[0,1,2],[1,0,2],[-1,1,2],[0,0,1]],
[[0,0,1],[1,0,2],[0,1,2],[-1,1,2]],
[[0,-1,2],[0,0,1],[1,0,2],[-1,1,2]],
[[-1,1,2],[0,1,2],[1,0,2],[0,0,1]],
[[-1,0,2],[-1,1,2],[0,1,2],[0,0,1]],
[[0,-1,2],[-1,0,2],[-1,1,2],[0,0,1]],
[[1,-1,2],[0,1,2],[1,0,2],[0,0,1]],
[[0,-1,2],[0,0,1],[1,0,2],[1,-1,2]],
[[-1,0,2],[0,-1,2],[0,0,1],[1,-1,2]],
[[0,-1,2],[1,0,2],[-1,1,2],[0,0,1]],
[[0,-1,2],[1,-1,2],[0,0,1],[-1,0,2]],
[[0,0,1],[0,-1,2],[1,-1,2],[-1,0,2]],
[[0,0,1],[1,0,1],[0,0,1],[0,0,1]],
[[-1,0,2],[1,0,2],[-1,0,2],[1,0,2]],
[[-1,0,2],[-1,0,2],[-1,0,2],[1,0,2]],
[[-1,0,2],[0,1,2],[0,0,1],[-1,1,2]],
[[-1,0,2],[0,0,1],[1,-1,2],[0,1,2]],
[[0,0,1],[0,-1,2],[1,-1,2],[1,0,2]],
[[0,-1,2],[-1,1,2],[0,0,1],[1,0,2]],
[[1,-1,2],[-1,0,2],[0,0,1],[0,1,2]],
[[1,0,2],[0,-1,2],[0,0,1],[-1,1,2]],
[[0,-1,2],[1,-1,2],[0,0,1],[1,0,2]],
[[0,0,1],[0,-1,2],[-1,1,2],[1,0,2]],
[[0,1,2],[-1,0,2],[-1,1,2],[0,0,1]],
[[-1,0,2],[0,-1,2],[0,0,1],[-1,1,2]],
[[-1,1,2],[0,-1,2],[1,0,2],[0,0,1]],
[[0,1,2],[0,0,1],[1,0,2],[1,-1,2]],
[[0,0,1],[0,0,1],[0,0,1],[1,0,1]],
[[-1,0,2],[-1,0,2],[1,0,2],[1,0,2]],
[[-1,0,2],[-1,0,2],[1,0,2],[-1,0,2]],
[[0,0,1],[1,-1,2],[1,0,2],[0,1,2]],
[[1,-1,2],[0,-1,2],[1,0,2],[0,0,1]],
[[1,-1,2],[-1,0,2],[0,0,1],[0,-1,2]],
[[0,0,1],[-1,0,2],[0,1,2],[-1,1,2]],
[[0,0,1],[0,-1,2],[-1,1,2],[-1,0,2]],
[[0,0,1],[1,-1,2],[-1,0,2],[0,-1,2]],
[[0,0,1],[-1,0,2],[0,1,2],[1,-1,2]],
[[-1,1,2],[-1,0,2],[0,0,1],[0,-1,2]],
[[-1,1,2],[0,0,1],[0,-1,2],[-1,0,2]],
[[0,0,1],[1,-1,2],[1,0,2],[0,-1,2]],
[[1,0,2],[0,0,1],[1,-1,2],[0,-1,2]],
[[1,0,2],[-1,1,2],[0,-1,2],[0,0,1]],
[[0,0,1],[0,0,1],[1,0,1],[0,0,1]],
[[-1,0,2],[1,0,2],[1,0,2],[-1,0,2]],
[[-1,0,2],[1,0,2],[-1,0,2],[-1,0,2]],
[[1,-1,2],[0,0,1],[0,1,2],[-1,0,2]],
[[0,-1,2],[-1,1,2],[0,0,1],[-1,0,2]],
[[-1,0,2],[-1,1,2],[0,-1,2],[0,0,1]],
[[-1,0,2],[0,0,1],[-1,1,2],[0,-1,2]],
[[0,-1,2],[0,0,1],[-1,0,2],[1,-1,2]],
[[1,-1,2],[0,0,1],[0,-1,2],[1,0,2]],
[[-1,0,2],[0,0,1],[1,-1,2],[0,-1,2]],
[[-1,0,2],[1,-1,2],[0,-1,2],[0,0,1]],
[[0,0,1],[1,-1,2],[-1,0,2],[0,1,2]],
[[1,-1,2],[0,0,1],[0,-1,2],[-1,0,2]],
[[0,0,1],[-1,0,2],[0,-1,2],[-1,1,2]],
[[-1,1,2],[-1,0,2],[0,0,1],[0,1,2]]
]
OK, reshared https://beta.observablehq.com/d/70a3c92c576398bb
see blue_struts... 60 (overlapping) blue struts, generated from a single blue vector
of course blue_struts is a total cheat, since I know the orientation of each strut that I generate
I think the lifelike one is busier than I'll want for many purposes
yes, but I think it is just a trick... not sure it would work for something so high-genus
one advantage of a headlamp... it banishes the shadows that we can't really render anyway 😉
that gives you the square of the quaternion of the rotation
so you can just square all the rotation quaternions, and then do a lookup
I think that would only be if the rotation was in the plane between the vectors directly. let me think about it
@Scott the "easy" way is to just do repeatedly chop the search space in half by doing tests against planes, and then have a map from the results of those tests -> orientations
and then can save time by caching any vectors that actually get used in a model
@Scott okay, I thought about it a bit, and I think the easiest way to find the 'orientation' is to use my same code to find the 'orbit', but record the signs along the way before I get rid of them. Each bit pattern (with some unused, and some aliasing in the case of zeros) will represent one original orientation. There were 3 + 2 + 2 + 1 = 8 bits involved (8 reflections to get every triangle on the sphere down onto one triangle), so if we want we can just use a small array of 8-bit numbers.
Only 120 of the 256 bit patterns will actually get used; we can leave the rest of the array full of zeros or whatever
Or instead of using an array of numbers, can directly use an array where the entries are a pair of [quaternion, reflectionbit], with reflectionbit = true meaning an improper rotation
depends whether you care about depending on some externally decided ordering of the rotations (e.g. vzome's)
remember what we did for the 'orbit' code was (1) take the absolute value of each coordinate [3 bits], (2) rotate around a red direction and take the absolute value again [2 bits, because one coordinate will always still be zero], (3) rotate around a red direction again and take the absolute value again [2 bits], check which side of the kite we are on and optionally reflect across a red-yellow great circle [1 bit]
It is easy to store a single permutation that maps jrus induces to vZome indices, and apply the at the moment of rendering
to figure out the association between quaternions and bit patterns, we can just take an orbit in the middle of the canonical triangle, apply each of the 60 quaternions, and record the bit pattern
and also want to combine the quaternion with a reflection across the origin and do it again, for the other 60 possibilities
this provides yet another (pretty arbitrary) numbering for the symmetries of the icosahedron
@Scott okay I think I mostly have this code debugged, but there are a few bits I'm still confused about
```orbit2 = { // return an array of [a < 0, abs(a)] const lt0abs = function lt0abs(a) { const [a0=0, a1=0, ad=1] = a, float = a0 + PHI * a1, sign = ((ad>0)-(ad<0)) * ((float>0)-(float<0)); return [+(sign < 0), [a0 * sign, a1 * sign, ad]]; }
const v = [[-1, 1], [-1], [0, -1]]; // φ⁻ -1 -φ return function orbit2(u) { // Reduce to the 'kite' shape at the corner of one octant: const bits = (new Array(8)).fill(0); u.slice(); [bits[0], u[0]] = lt0abs(u[0]); [bits[1], u[1]] = lt0abs(u[1]); [bits[2], u[2]] = lt0abs(u[2]); bits[1] ^= bits[0]; bits[2] ^= bits[0]; // make sure reflections have the same lower-order bits. u = transform(redrotation, u); [bits[3], u[1]] = lt0abs(u[1]), [bits[4], u[2]] = lt0abs(u[2]); u = transform(redrotation, u); [bits[5], u[1]] = lt0abs(u[1]); [bits[6], u[2]] = lt0_abs(u[2]);
// Now reflect the top triangle of the kite onto the bottom triangle:
let d = dot(u, v), ds = +grgreater([0], d); bits[7] = ds;
d = grmul([-ds, 0, 2], d); u = vectoradd(u, scalarmul(d, v));
// Now return rational coordinates of a gnomonic (tangent) projection.
// return scalarmul(grinv(u[0]), [u[1], u[2]]);
return bits.reduce((a, b) => (a << 1) + b)
} }```
@Scott the prototypes for different struts are largely not normalized to the same canonical triangle, right?
It's also not clear to me how you chose the canonical lengths
@Scott so okay, given 2 endpoints, what info do I need to generate so you can render a strut?
I think we need (1) orbit, (2) orientation [rotation and also reflection?], (3) scale vs. a prototype strut
@Scott how much problem would it be to require the prototype struts to all be in the same canonical triangle between blue (1, 0, 0), yellow (1, 0, φ⁻²), red (1, φ⁻, 0) ?
I guess it doesn't have to be that way; just if it isn’t then we need to look up what orientation the prototype strut does have, and rotate the given strut twice to get there so we can compare lengths
okay cleaned this up a little ```orbit2 = { // Return (a < 0); as a side effect, change a to abs(a), in place. const lt0abs = function lt0abs(a) { const float = a[0] + PHI * a[1], sign = ((float>0)-(float<0)); a[0] *= sign; a[1] **= sign; return +(sign < 0); }
const v = [[-1, 1], [-1], [0, -1]]; // φ⁻ -1 -φ
return function orbit2(u) { u = [[...u[0]], [...u[1]], [...u[2]]]; // make a deep copy of 'u'.
// Reduce to the 'kite' shape at the corner of one octant:
const bits = [lt0_abs(u[0]), lt0_abs(u[1]), lt0_abs(u[2])];
bits[1] ^= bits[0]; bits[2] ^= bits[0]; // make sure reflections have the same lower-order bits.
u = transform(red_rotation, u); bits.push(lt0_abs(u[1]), lt0_abs(u[2]));
u = transform(red_rotation, u); bits.push(lt0_abs(u[1]), lt0_abs(u[2]));
// Now reflect the top triangle of the kite onto the bottom triangle:
let d = dot(u, v), ds = +grgreater([0], d); bits.push(ds);
d = grmul([-ds, 0, 2], d); u = vectoradd(u, scalarmul(d, v));
// Now return rational coordinates of a gnomonic (tangent) projection.
// return scalarmul(grinv(u[0]), [u[1], u[2]]);
return bits.reduce((a, b) => (a << 1) + b);
} }```
I have a mechanism in vZome to canonicalize struts in that fashion. Many of the prototypes were historically defined without regard to this question... Likewise the lengths. I agree we should have new code work only with canonical definitions, whether it be orientations, lengths, prototypes, or whatever. I will change the VSON exporter to align. The only thing I'm really concerned about preserving is orbit names and colors. Other than that, let's not let past mistakes weigh us down. We are defining a new model interchange format, with decoupled shapes.
@Scott well the easiest would be to make the canonical lengths always be coordinates (1, a, b)
but that is going to add unnecessary denominators you might not really want
but the nice thing about it would be that finding the relative length is super easy. just rotate any arbitrary strut to be in the canonical triangle and then look at the x coordinate
it's not that much harder to just use any arbitrary length within that triangle. then to find relative lengths just divide the x coordinates
it would certainly be possible as an alternative to use an arbitrary direction, and just make sure to rotate both struts to the canonical direction before dividing
@Scott I think I'm going to normalize all the quaternions to have positive scalar component
I'll think about it. The "usable unit" is already decoupled in vZome, so I may have the freedom. I have always preferred to define orbits with "nice" coordinates... Like 1,1,1 for yellow, though I don't think I use that, actually
there's nothing wrong with [1,1,1] for yellow... except that it isn't near [1,0,0]
here are the quaternions for each bit string coming from my code:
000 [[-1,1,2],[0,-1,2],[-1,0,2],[0,0,1]]
001
002 [[0,0,1],[1,0,2],[0,-1,2],[1,-1,2]]
003 [[0,1,2],[-1,0,2],[1,-1,2],[0,0,1]]
004 [[1,0,2],[0,0,1],[-1,1,2],[0,-1,2]]
005 [[1,0,2],[1,0,2],[1,0,2],[1,0,2]]
006 [[0,1,2],[-1,1,2],[0,0,1],[1,0,2]]
007
008
009
010 [[[1,0,1],[0,0,1],[0,0,1],[0,0,1]]
011 [[0,0,1],[-1,1,2],[-1,0,2],[0,-1,2]]
012
013
014
015
016 [[1,0,2],[1,-1,2],[0,1,2],[0,0,1]]
017 [[1,0,2],[-1,0,2],[-1,0,2],[-1,0,2]]
018 [[0,0,1],[0,1,2],[-1,1,2],[1,0,2]]
019 [[-1,1,2],[0,0,1],[0,1,2],[-1,0,2]]
020 [[0,1,2],[0,0,1],[-1,0,2],[-1,1,2]]
021
022 [[-1,1,2],[1,0,2],[0,0,1],[0,-1,2]]
023
024
025
026
027 [[1,0,2],[0,1,2],[0,0,1],[-1,1,2]]
028
029
030
031
032 [[0,0,1],[1,0,2],[0,-1,2],[-1,1,2]]
033
034 [[-1,1,2],[0,1,2],[1,0,2],[0,0,1]]
035 [[0,0,1],[-1,1,2],[-1,0,2],[0,1,2]]
036 [[0,1,2],[1,-1,2],[0,0,1],[1,0,2]]
037 [[1,0,2],[1,0,2],[-1,0,2],[-1,0,2]]
038 [[1,0,2],[0,0,1],[1,-1,2],[0,-1,2]]
039
040
041
042 [[0,0,1],[0,0,1],[0,0,1],[1,0,1]]
043 [[0,1,2],[1,0,2],[-1,1,2],[0,0,1]]
044
045
046
047
048 [[0,0,1],[0,1,2],[-1,1,2],[-1,0,2]]
049 [[1,0,2],[1,0,2],[-1,0,2],[1,0,2]]
050 [[1,0,2],[-1,1,2],[0,-1,2],[0,0,1]]
051 [[1,0,2],[0,-1,2],[0,0,1],[-1,1,2]]
052 [[-1,1,2],[-1,0,2],[0,0,1],[0,-1,2]]
053
054 [[0,1,2],[0,0,1],[1,0,2],[-1,1,2]]
055
056
057
058
059 [[-1,1,2],[0,0,1],[0,-1,2],[-1,0,2]]
060
061
062
063
064 [[1,0,2],[0,0,1],[-1,1,2],[0,1,2]]
065
066 [[0,1,2],[1,-1,2],[0,0,1],[-1,0,2]]
067 [[-1,1,2],[0,0,1],[0,1,2],[1,0,2]]
068 [[-1,1,2],[0,1,2],[-1,0,2],[0,0,1]]
069 [[1,0,2],[-1,0,2],[-1,0,2],[1,0,2]]
070 [[0,0,1],[1,0,2],[0,1,2],[1,-1,2]]
071
072
073
074 [[0,0,1],[0,0,1],[1,0,1],[0,0,1]]
075 [[1,0,2],[0,-1,2],[0,0,1],[1,-1,2]]
076
077
078
079
080 [[0,1,2],[0,0,1],[-1,0,2],[1,-1,2]]
081 [[1,0,2],[-1,0,2],[1,0,2],[1,0,2]]
082 [[-1,1,2],[-1,0,2],[0,0,1],[0,1,2]]
083 [[0,1,2],[1,0,2],[1,-1,2],[0,0,1]]
084 [[1,0,2],[-1,1,2],[0,1,2],[0,0,1]]
085
086 [[0,0,1],[0,1,2],[1,-1,2],[1,0,2]]
087
088
089
090
091 [[0,0,1],[-1,1,2],[1,0,2],[0,-1,2]]
092
093
094
095
096 [[0,1,2],[-1,1,2],[0,0,1],[-1,0,2]]
097
098 [[1,0,2],[0,0,1],[1,-1,2],[0,1,2]]
099 [[1,0,2],[0,1,2],[0,0,1],[1,-1,2]]
100 [[0,0,1],[1,0,2],[0,1,2],[-1,1,2]]
101 [[1,0,2],[-1,0,2],[1,0,2],[-1,0,2]]
102 [[-1,1,2],[0,-1,2],[1,0,2],[0,0,1]]
103
104
105
106 [[0,0,1],[1,0,1],[0,0,1],[0,0,1]]
107 [[-1,1,2],[0,0,1],[0,-1,2],[1,0,2]]
108
109
110
111
112 [[-1,1,2],[1,0,2],[0,0,1],[0,1,2]]
113 [[1,0,2],[1,0,2],[1,0,2],[-1,0,2]]
114 [[0,1,2],[0,0,1],[1,0,2],[1,-1,2]]
115 [[0,0,1],[-1,1,2],[1,0,2],[0,1,2]]
116 [[0,0,1],[0,1,2],[1,-1,2],[-1,0,2]]
117
118 [[1,0,2],[1,-1,2],[0,-1,2],[0,0,1]]
119
120
121
122
123 [[0,1,2],[-1,0,2],[-1,1,2],[0,0,1]]
124
125
126
127
to tell if a reflection is needed, count up the number of 1s in the bit string
It still bothers me to go to floats for this. Every other aspect I like
it's not going to matter for the sizes of integers that javascript can deal with
the alternative is to do everything in some custom arbitrary precision integer type
and then run the Euclidean algorithm every time to determine the sign
personally I'd rather not resort to that since it makes everything substantially more complicated
Also I don't know how one applies the Euc alg to Golden rationals
at that point you definitely want a new type in a library somewhere
Not enough BW here... Talk later. Leaving work shortly
you need to determine if the ratio between a and b is bigger or smaller than -phi
you can basically turn the ratio into a continued fraction, and as soon as you hit a digit that isn’t 1, you stop
for instance, if you started with 8/5, you do 8/5 = 1 + 3/5 = 1 + 1/(1 + 2/3) = 1 + 1/(1 + 1/(1 + 1/2))
the parity tells you whether your ratio was bigger or smaller than the golden ratio
figuring out the precise details of the algorithm and making sure it works in all edge cases might be a pain, I'm not sure
probably not inordinately difficult, but it'll probably take me an hour or something to do
[I didn't meant to imply I would actually do this any time soon]
```orbit2 = { // Return (a < 0); as a side effect, change a to abs(a), in place. const lt0abs = function lt0abs(a) { const float = a[0] + PHI * a[1], sign = ((float>0)-(float<0)); a[0] *= sign; a[1] **= sign; return +(sign < 0); }
// Parity of the number of ones of an 8-bit integer in binary const hammingparity = function hammingparity(n) { n -= (n >> 1) & 0x55; n -= (n >> 2) & 0x33; return (n - (n >> 4)) & 0x01; }
const v = [[-1, 1], [-1], [0, -1]]; // φ⁻ -1 -φ
return function orbit2(u) { u = [[...u[0]], [...u[1]], [...u[2]]]; // Make a deep copy of 'u'.
// Reduce to the 'kite' shape at the corner of one octant:
const bits = [lt0_abs(u[0]), lt0_abs(u[1]), lt0_abs(u[2])];
bits[1] ^= bits[0]; bits[2] ^= bits[0]; // Make sure reflections have the same lower-order bits.
u = transform(red_rotation, u); bits.push(lt0_abs(u[1]), lt0_abs(u[2]));
u = transform(red_rotation, u); bits.push(lt0_abs(u[1]), lt0_abs(u[2]));
// Now reflect the top triangle of the kite onto the bottom triangle:
let d = dot(u, v), ds = +grgreater([0], d); bits.push(ds);
d = grmul([-ds, 0, 2], d); u = vectoradd(u, scalarmul(d, v));
const orb = scalarmul(grinv(u[0]), [u[1], u[2]]); // rational coordinates of a gnomonic projection
const bits_int = bits.reduce((a, b) => (a << 1) + b);
const quat = orbit_quaternions[bits_int & 127];
const reflected = hamming_parity(bits_int);
return [orb, quat, reflected];
} }```
for struts along the boundary of the canonical triangle, this is going to give us a somewhat arbitrary one of the aliased orientations
@Scott I'm going to eat, will probably be back in an hour or two... when I get back you can maybe help me figure out exactly what we need to convert a pair of points -> a form that we can directly render
i.e. a pair of points representing nodes which should be connected by a strut
@Scott how well does it work if instead of providing orbit by name, orientation by number, and length relative to an externally described vector, if we provide (1) orbit by canonical projective coordinates, (2) orientation by explicit quaternion, and (3) length as the length of the 1st coordinate when the vector has been rotated into the canonical triangle
we won't need the reflection bit... more when we talk
the former one gets rid of the brackets for an array, so there's some potential for cases where it loses information
the logic is implemented in some compiled language and heavily optimized
I think quats instead of orbit indices is fine... eliminate dependency
I think we could do better if we used a custom type built out of a typed array or whatever, but I think this should be fine for any model with less than millions of objects
I think we can just generate a new vertex whenever we need to generate either end ball for a strut... not worry about duplication
@Scott okay, so let me figure out what the orientation quaternions are of all of your prototype struts
and then I can try to transform them all to jacob-standard-form, and make a lookup of orbit projective coordinates -> color names
@Scott PS I did almost no pre-planning of my function names, so they are probably terribly inconsistent, and possibly just terrible in general
@Scott if you have any ideas for renames, I'm happy to oblige
@Scott did I screw something up about how blue_struts render?
@Scott oh here's an interesting one. it turns out that I may need more than 60 entries in my array after all; apparently for vectors on the edge of the canonical triangle, the bit pattern turns out differently than for vectors in the interior
Hmmm, forgot to ask you about that... most of our vectors will fall on the mirrors, of course
I also had a question about behavior of floating point ops w.r.t. sign errors... can precision error ever affect the sign?
as for function names, I think I'll want to introduce the field concept sooner or later... so "grfloat" will be bound as "goldenNumber.evaluate", and so on
or rather "field.evaluate", since field will be assigned goldenField
not describing it well... just an object containing functions
@Scott there should only be a sign error for numbers > 2^53
@Scott we should get a sign error for anything smaller than φ^(–78)
@Scott actually though, some of the code might convert stuff to 32-bit integers
in that case you might start getting weirdness at φ^(–47)
@Scott if you need to make models spanning 50 powers of φ, we can talk about some arbitrary precision integer types
now I have extruded edges of a dodec, rather than the rays of an icosido
oh, or you are describing the results, rather than the intent 🙂
I got sidetracked by simon. yao is asleep. we'll see if he lets me work or demands I read more books 🙂
struggling with spread syntax for arbitrary object keys
icosahedralsolidconnectors_shapes.strutGeometries...
keyed by orbit name, but I need to parse the pieces of each shape object (the value space)
https://stackoverflow.com/questions/14810506/map-function-for-objects-instead-of-arrays
you can do stuff like {a, b} = foo, and that is a shortcut for a = foo.a, b = foo.b;
one thing to note, {...object} syntax doesn't work in safari or edge
let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] ** obj[c]}), {});
Chrome falls over hard with as many browser tabs as I have
people's notebooks using {...foo} are constantly annoying 🙂
I think people are used to writing code with babel or whatever
letting some other layer convert their modern javascript to a form that old browsers can deal with
the spread syntax for objects isn't yet in a finalized spec I think
I'm sure safari and edge will support it within a year or two
I saw that... and Observable doesn't handle it for us? It does its own parsing, right?
it doesn't rewrite it all to work across arbitrary browsers
I'm fading... reshared, with the shape creation refactored as you suggested. Also reduced object creation for balls
I'll see if I can remap your default struts to standard orientation
@Scott I think there might literally be only one orbit that wasn't in there before. the (1, 0, 0) direction, which you get from an identity matrix
but of course the blue strut was the first one I looked at, and got a 'undefined' for the quaternion
erm, I am using the word 'orbit' wrong in the above; I meant 'orientation'
@Scott oh! I know what I broke in your code. I didn't realize you were using quattransform
I can switch that back if you want, but I think it's more obvious if it takes a 3-element input
blue_struts = ({
vertices: icosahedral_quaternions_vzome .map( q => quattransform( q, [[-3,-5,1],[0],[0]] ) )
.concat( [[ [0],[0],[0] ]] ),
balls: icosahedral_quaternions_vzome .map( (q,index) => index ) .concat( [ 60 ] ),
struts: icosahedral_quaternions_vzome .map( (q,index) => ({
start: index,
length: [3,5,1],
orbit: "blue",
orientation: index
}))
})
I have reshared. Normalization of vZome exports is completed, and I believe I have removed all the object spreads.
Reshared again. Quaternions are now part of the model, as an indexed array.
@Scott last night I got confused by the edge case behavior of the orbit stuff
ok, no worries... I'm still busy with more normalization
I had started in on normalizing your strut prototypes to the same fundamental triangle
by "part of the model", I meant that the input model for vZomeRenderer now requires a "quaternions" indexed array... each strut "orientation" is still an index into that array
whatever issue you hit with the prototypes, I'll probably hit soon.
I'm trying to switch from the old orbit names to the new
@Scott
{
"[[0,0,1],[0,0,1]]": "blue",
"[[-1,1,1],[0,0,1]]": "red",
"[[0,0,1],[2,-1,1]]": "yellow",
"[[2,-1,1],[5,-3,1]]": "green",
"[[-4,3,5],[3,-1,5]]": "orange",
"[[2,-1,1],[0,0,1]]": "purple",
"[[-2,3,11],[-7,5,11]]": "black",
"[[-3,2,1],[-3,2,1]]": "lavender",
"[[3,-1,5],[0,0,1]]": "olive",
"[[5,-3,1],[0,0,1]]": "maroon",
"[[0,0,1],[-4,3,5]]": "rose",
"[[-1,1,2],[2,-1,2]]": "navy",
"[[2,-1,2],[-3,2,2]]": "turquoise",
"[[-3,2,2],[-1,1,2]]": "coral",
"[[-1,1,3],[0,0,1]]": "sulfur",
"[[-8,5,1],[5,-3,1]]": "sand",
"[[2,-1,3],[-1,1,3]]": "apple",
"[[5,-3,2],[2,-1,2]]": "cinnamon",
"[[-5,4,11],[-5,4,11]]": "spruce",
"[[2,-1,3],[5,-3,3]]": "brown",
}
yes, I have already reindexed my strut shapes in that way
your instability is in the quaternion output? or the sign bit? or both
the quaternion output is not necessarily reliable in edge cases
@Scott canonicalizing some of the strut directions & lengths definitely makes them uglier. apparently the canonical 'black' strut is in a reflected direction. after improper rotation we go [[0], [1], [1,-1]] -> [[-1,3,2], [1,0,2], [2,-1,2]]
but then dividing by the first coordinate gets us [[-2,3,11], [-7,5,11]]
it is a 90-degree rotated blue... or a blue in the 90-degree rotated icosahedron, if you like
I'm going to try (at some point in the next few days/weeks) to get all these orbits plotted as great circles on the sphere / gnomonic projection / stereographic projection. we can see what patterns we see
My renderer is working at the moment by ignoring strut.orbit, and computing it from end-start
Yes, I really want to have that great circle viewer... even with bad mouse rotation
@Scott so I was thinking about it, the strut geometry shouldn't care whether rotation happens before or after stretching, etc., right?
by stretching I mean displacing the 'full length vertices' etc.
oh wait, I'm confusing myself. you want to cache them by length
I think again it comes back to building rotated geometries (lots more, in other words), vs. letting the graphics card do the rotations of one geometry
but anyhow, my basic point of my line of thought was: I should be able to normalize your existing strut models by just rotating all the vertex coordinates by the appropriate quaternion
the quaternions are pretty well determined by the symmetry system
luckily I think we both have a right-hand coordinate system. otherwise you might get a literal necker cube
with my strut shapes as they are, we have to use my orbit prototypes, right?
it is working for me, but only because I apply orbit2 both to a strut vector, and to my orbit prototypes, to define the mapping
you need to (1) find the icosahedral rotation that takes your orbit prototype -> a 'jacob-canonical' vector [this is just the conjugate of the rotation that goes the other way], (2) apply that rotation in composition with whatever other rotation to move the strut to its final orientation when drawing
so I guess there is no reason to change, as long as we index by the canonical orbit name
btw, your strut colors are super hard to figure out the names for just from the colors 🙂
I forget the names myself... tried to be somewhat consistent around material concept, but it ended up pretty arbitrary
there are 3 similar purple ones, two types of salmony colors differing only by lightness/chroma, 4 typesof brown, 2 oliveish greens
the reason I didn't use more of the color space is that I was trying to avoid pastels, for a long time... since those were reserved for automatic orbits
I guess I just mean yellowish greens. there's like "light side of a leaf green", "dark side of a leaf green",and "olive green"
the names never mattered much except internally as keys
I'm teasing you, but I might want to move your colors around a bit
cinnamon is too close to maroon and brown... that was a bad choice
that is, turquoise, the brighter purple, the hot pink one, the neon chartreuse one
yes... the problem being that balanced brightness is actually counterproductive when trying to distinguish
we can just tone down the chroma (colorfulness) without changing the lightness or hue
but too much variation, and then how do you highlight a selection?
I have fiddled with this kind of thing before with a lot of theory. it's always a pain
I'm OK making tweaks... just a little defensive of this as part of the vZome "brand", I guess
I don't need to clobber your colors. I just want to make a few of them more distinct and a few of them less colorful (maybe a few of them more colorful)
at least I know you won't ask me to make orange struts red, as Marc did
sure, pretty important. Red in a 90-degree rotated icosahedron. Lies in a red plane, too. Pops up a lot in 4D projections
I'm still struggling with the orbit / quaternion thing, a bit
at some point (and not necessarily for vzome) I'm excited to experiment with that 'plastic sequence' thingy or something related, to see if I can get an auto-generated list that starts out with a few good looking colors and then keeps generating ideally distinct ones indefinitely
I'd like to reintroduce coloring of automatic orbits, based on that
let's use the term "resolve" to mean what orbit2 does...
just reshared... right now, the renderer resolves a strut based on end-start, but then ignores the quaternion returned, using only the orbit to look up the shape (and its prototype)
alright, I'm going to systematically go through all 120 symmetries of 7 example orbits, checking what bit pattern my code dumps out for each one, and make sure that they are all compatible
test_orbits = [
[[[2,-1,2],[-3,2,2]], "turquoise"],
[[[2,-1,1],[5,-3,1]], "green"],
[[[0,0,1],[-4,3,5]], "rose"],
[[[2,-1,1],[0,0,1]], "purple"],
[[[-1,1,1],[0,0,1]], "red"],
[[[0,0,1],[2,-1,1]], "yellow"],
[[[0,0,1],[0,0,1]], "blue"],
]
the quaternion used is from icosahedralquaternionsvzome, as indexed by strut.orientation
this is upside down... red, yellow, blue are literally corner cases, but figuratively not!
turquoise (or better, spruce) is a non-edge case literally, but it arises rarely, so it is a figurative edge-case
spruce, by the way, has a special property... it probably corresponds to some form of center of the domain
I went for turquoise because it's easier to match what I see on vzome to the name
If you apply icosahedral symmetry to a spruce strut, then build faces to panel the vertices... the faces are orthogonal to spruce.
In other words, a spruce line exits from a vertex of that object, but re-enters orthogonal to a face.
I'm somewhat surprised you don't have any other orbits between blue and yellow except "rose"
I guess it's a bit like rational numbers: the main orbits repel other ones a bit
Yes I think so. Not many great circle crossings there
actually, if there's some systematic way to generate orbits and heuristically judge their 'simplicity', we could make a picture like the zigzag chart
where we draw 10k orbits (or whatever) as great circles, fading gradually to white
the Synergetics folks always talk about them by generations... RYB circles intersect to create orange, purple, green orbits, and their planes intersect further, and so on
that would be a good scheme for generating the planes... I have some friends that would no doubt see God (or something) in such a figure
I would probably just list all the combinations of golden-integer coordinates in 3-space, order them by how big the numbers in the coordinates get, then remove duplicates
I think we'd see surprising amounts of overlap between the two approaches
just doing all the repeated intersections is more complicated to code
wow, there's one improper rotation for which every one of my 7 orbits returns a different bit string
for the edge cases, multiple different rotations send the orbit to the same direction (aliasing)
so when you start with a direction and work back to find the rotation, you will only pick out one of those
yes, now I know what you were referring to. The reason why five different quaternions or matrices will work equally well to render a red strut
FWIW, I fixed the VSON exporter. The all_turquoise model is now rendered correctly. Reshared.
@jrus OK I have finished my refactoring. vZomeRenderer now accepts a model like the following:
The "end" index is optional, not required for rendering.
I have also implemented resolveModel, using orbit2 to go from a list of vertex pairs to a model of this form.
https://beta.observablehq.com/d/70a3c92c576398bb#resolveModel
My test, using endpoints of a single blue strut and a single yellow, gives me one "undefined" quaternion for blue... I guess this is the problem you're working on
https://beta.observablehq.com/d/70a3c92c576398bb#testResolve
Predictably, as you can see in the rendering, the yellow strut is going in the wrong direction. I handle the missing quaternion, but for some reason I don't get a blue strut.
I think I can get it to work out consistently for any arbitrary strut, but I'm doing some sort of "factual investigation" to see what the code actually does in various edge cases
@Scott okay, was gone for a while. I think I was attacking this the wrong (needlessly cute) way. Instead I’m going to just work through the transformations implied by each potential reflection applied during my orbit code, and then compose them all together for each possible pattern of reflections
some of the patterns won't ever happen in practice, but it's okay to leave entries in the table for them
OK, perhaps you can show me tomorrow. I'd like to understand better, if possible.
@Scott something along the lines of:
let quat = [[1],[0],[0],[0]];
if (b & 0x80) quat = quatmul(quat, [[0],[1],[0],[0]]);
if (b & 0x40) quat = quatmul(quat, [[0],[0],[1],[0]]);
if (b & 0x20) quat = quatmul(quat, [[0],[0],[0],[1]]);
quat = quatmul(quat, [[0,1,2],[1,0,2],[-1,1,2],[0,0,1]]);
if (b & 0x10) quat = quatmul(quat, [[0],[0],[1],[0]]);
if (b & 0x08) quat = quatmul(quat, [[0],[0],[0],[1]]);
quat = quatmul(quat, [[0,1,2],[1,0,2],[-1,1,2],[0,0,1]]);
if (b & 0x04) quat = quatmul(quat, [[0],[0],[1],[0]]);
if (b & 0x02) quat = quatmul(quat, [[0],[0],[0],[1]]);
if (b & 0x01) quat = quatmul(quat, [[0,0,1],[-1,1,2],[-1,0,2],[0,-1,2]]);
basically we look at each bit (representing a potential reflection), and if it’s true then we rotate by a quaternion that puts us exactly opposite from that reflection. (basically we treat opposite orientations on the sphere as the same, for the purpose of this code). remember at the end we decide whether to do a final reflection across the origin by counting up all the reflection bits and checking the parity
I might be multiplying these in the wrong order or on the wrong side (right vs. left multiplication), I'll figure it out in the morning
then I just can build up a table by starting with every possible value of b between 0 and 255
I'm going to try a different approach, minimizing spread. Similar to my brute-force vZome approach, but using some of your ideas to reduce the search space from 60 comparisons down to 12. I'll have to do one last sign check on a non-zero coordinate value, to determine the "sense" within the zone.
@Scott https://beta.observablehq.com/@jrus/zome-arithmetic#orbit2
I swapped it to orbit, quaternion, reflection bit, then length (length relative to the projection of the standard orientation onto x=1 plane)
but maybe I should output a JS object instead of array, dunno
this thing is pretty much self-contained:
```orbit2 = { // Return (a < 0); as a side effect, change a to abs(a), in place. const lt0abs = function lt0abs(a) { const float = a[0] + PHI * a[1], sign = ((float>0)-(float<0)); a[0] *= sign; a[1] **= sign; return +(sign < 0); }
// Parity of the count of ones of an 8-bit integer in binary const hammingparity = function hammingparity(n) { n -= (n >> 1) & 0x55; n -= (n >> 2) & 0x33; return (n - (n >> 4)) & 0x01; }
// Build up a lookup table for bit patterns -> quaternions const orbit_quaternions = [...range(0x100)].map(b => { let quat = [[1],,,]; if (b & 0x80) quat = quatmul(quat, [,[1],,]); if (b & 0x40) quat = quatmul(quat, [,,[1],]); if (b & 0x20) quat = quatmul(quat, [,,,[1]]);
quat = quatmul(quat, [[,1,2],[-1,,2],[1,-1,2],]);
if (b & 0x10) quat = quatmul(quat, [,,[1],]);
if (b & 0x08) quat = quatmul(quat, [,,,[1]]);
quat = quatmul(quat, [[,1,2],[-1,,2],[1,-1,2],]);
if (b & 0x04) quat = quatmul(quat, [,,[1],]);
if (b & 0x02) quat = quatmul(quat, [,,,[1]]);
if (b & 0x01) quat = quatmul(quat, [,[-1,1,2],[-1,,2],[,-1,2]]);
const sign = quat.map(grsign).reduce((a, b) => a || b) || 1;
return scalarmul([sign], quat);
});
const v = [[-1, 1], [-1], [, -1]]; // φ⁻ -1 -φ
// Find the orbit and orientation for a vector u. return function orbit2(u) { u = [u[0].slice(), u[1].slice(), u[2].slice()]; // Make a deep copy of 'u'.
// Reduce to the 'kite' shape at the corner of one octant:
const bits = [lt0_abs(u[0]), lt0_abs(u[1]), lt0_abs(u[2])];
u = transform(red_rotation, u); bits.push(lt0_abs(u[1]), lt0_abs(u[2]));
u = transform(red_rotation, u); bits.push(lt0_abs(u[1]), lt0_abs(u[2]));
// Now reflect the top triangle of the kite onto the bottom triangle:
let d = dot(u, v), ds = +grgreater([0], d); bits.push(ds);
d = grmul([-ds, 0, 2], d); u = vectoradd(u, scalarmul(d, v));
const orb = scalarmul(grinv(u[0]), [u[1], u[2]]); // rational coordinates of a gnomonic projection
const bits_int = bits.reduce((a, b) => (a << 1) + b);
const rotation = orbit_quaternions[bits_int];
const length = u[0];
const reflected = hamming_parity(bits_int);
return [orb, rotation, reflected, length];
} }```
not as bad as I feared... thought you would have 60 lines of lookup table
@Scott one thing to note: for some edge cases, this picks the triangle on the side which is reflected vs. the standard triangle
so often it will report a 'reflection' even when there is some rotation which would send that strut pointed the other way
so you just need to always look at the reflected bit before deciding what zone the strut is in
reflected bit won't affect zone... just whether the strut goes (start,end) or (end,start)
basically there are two 'zones' adjacent to any vector on edge between zones, or more for the corners
the quaternions are different between zones, but they send the vector to the same place
maybe I should add an optional 'reflect' parameter to quattransform
though if you need to handle this by flipping which vertex is the start, etc. maybe that's not helpful to you
sometime later, I'll try to normalize all the strut models
I think I can output a little more info there, since I have internal normalization already... I still need to check
@Scott actually, I think I can avoid the edge-case-reflections thing if I record the signs along the way as {–1, 0. 1}, and then if there are any zeros and an odd number of –1s, set one of the zeros to –1
in that case, vectors on an edge would always be reported as a pure rotation of the canonical vector
@jrus I have accommodated your output change in my resolveModel... now rendering the testResolve result. Still has my scaling problems and the shape orientation issue, but no worries.
https://beta.observablehq.com/d/70a3c92c576398bb#parseAlgebraicNumber
I don't know why there is no blue strut... I'll work it out
@Scott okay I'm going to try to look at your json gists and try to convert the orbits and strut shapes to canonical directions
I'll adjust 'unitlength' in the orbit to match vzome after the prototype is scaled
in your orbit2 function? or in resolveModel (or equivalent)?
or maybe the prototype strut should just be adjusted to be in the canonical orbit but have vzome-unit length
a negative sign means to apply that rotation matrix, then reverse the sense
btw, simon is now saying "icosahedron", and it's pretty amusing
@Scott maybe you want to use 3-integer golden rationals? along the lines of https://gist.github.com/jrus/5995e70f5a6932d0f1b0ed6040415ee5
yes, eventually... remember I'm normalizing all that in my notebook
you can consume the normalized version, rather than the Gist
but I think it's clearer to just hand edit it at this point
I also think these json blobs are short enough to just go in a notebook cell
they get annoying in a cell... too much scrolling if I open the cell accidentally
I've updated the Gist to display the perms and matrices better... more vertical space, ironically 😉
it can be a different notebook. "raw zome data" or whatever
I just recommend putting it in the same notebook at the very bottom
aha, maybe the models themselves are too big to fit in a notebook cell
https://beta.observablehq.com/d/70a3c92c576398bb#golden_icosahedral_symmetry
I'll incorporate the "canonicalize" into convertShapes
but I think now I have to go shopping for garden stuff with Bella
bella is your wife? (I didn't really get to chat with her)
we'll get another chance, next time we come up to the city
@Scott btw, when you do foo.map(x => bar(x)), you can instead just write foo.map(bar)
(though admittedly the former one makes it easier to add stuff to the map function later)
I definitely am glad that javascript added the arrow functions. it used to be foo.map(function (x) { return bar(x); })
@Scott oh let's adjust parseAlgebraicNumber so that it can handle 3-element numbers as well
function parseAlgebraicNumber([a0, a1, ad=1]) {
let d0 = 1, d1 = 1;
if (!(typeof a0 === "number")) [a0, d0] = a0.split('/').map(Number);
if (!(typeof a1 === "number")) [a1, d1] = a1.split('/').map(Number);
return simplify3(a0**d1, a1**d0, d0**d1**ad);
}
or better
function parseAlgebraicNumber([a0=0, a1=0, ad=1]) {
let d0 = 1, d1 = 1;
if (!(typeof a0 === "number")) [a0, d0] = a0.split('/').map(Number);
if (!(typeof a1 === "number")) [a1, d1] = a1.split('/').map(Number);
return simplify3(a0**d1, a1**d0, d0**d1**ad);
}
then if you convert some of the numbers in your json, it won't care
@Scott okay, not yet rotated to standard form, but all in 3-value integer form https://gist.github.com/jrus/5995e70f5a6932d0f1b0ed6040415ee5#file-solid_connectors-json
maybe it's better to just leave the vertices for each one on a single line in the file though, dunno...
I already had convertShapes... Something else? Not at my computer (shopping==waiting)
I'm going to rotate all of the shapes to align with a canonical-triangle-oriented strut
the only 'something else' is me trying to make the json human-legible, but maybe that's not useful
@Scott okay I indexed by orbit coordinates, rotated all the vertices, and put all the prototype vectors in [1, a, b] form https://gist.github.com/jrus/5995e70f5a6932d0f1b0ed6040415ee5#file-solid_connectors_normalized-json
@Scott it's possible that I rotated the vertices to the wrong direction or something. I'll have to try actually drawing some models 🙂
did you look at my converted shapes? I think they were only missing the rotation
also I renamed fullScaleVertices to "tip" and halfScaleVertices to "waist"
I want to update convertShapes to do the rotations... can you share the code you used to generate?
{
const shapejson = JSON.parse(JSON.stringify(solid_connectors)); // deep copy
const outstruts = {}
for (const name in shapejson.strutGeometries) {
const strut = shapejson.strutGeometries[name];
const [orb, rotation, reflected, length] = orbit2(strut.prototypeVector);
const vertices = strut.vertices.map(v => {
v = quattransform(quatconj(rotation), v);
if (reflected) v = scalarmul([-1], v);
return v;
})
const outstrut = Object.assign({'name': name}, strut, {
'vertices': vertices, 'prototypeVector': [[1,0,0]].concat(orb)});
outstruts[JSON.stringify(orb)] = outstrut;
}
return outstruts;
}
@Scott that one isn't doing the parseAlgebraicNumber stuff
np, got that already.... just cherry-picking pieces of it
hmmm... @jrus neither of our notebooks are loading for me. Have we introduced a cycle?
if you have a big screen or two screens, I also recommend opening two windows
so you can then tick them closed in the view side if you want
yes, I've struggled with search... another good reason
lol... you put in "x" to mark code you're not ready to compile yet?
it just disables evaluation of the cell by causing a syntax error
stuff that has a big blob of output I don't want to see, etc.
or stuff that I broke, but don't want to completely delete yet
oh, I should start using Object.entries(obj).forEach( ([key, value]) => ... );
e.g.
{
const shapejson = JSON.parse(JSON.stringify(solid_connectors)); // deep copy
return Object.entries(shapejson.strutGeometries).map(([name, strut]) => {
const [orb, rotation, reflected, length] = orbit2(strut.prototypeVector);
const vertices = strut.vertices.map(v => {
v = quattransform(quatconj(rotation), v);
return (reflected) ? scalarmul([-1], v) : v;
})
const outstrut = Object.assign(
{'name': name}, strut,
{'vertices': vertices, 'prototypeVector': [[1,0,0]].concat(orb)});
return [JSON.stringify(orb), outstrut];
}).reduce((map, [key, val]) => (map[key] = val, map), {});
}
@Scott maybe this is too illegible:
icosahedral_quaternions_vzome2 = {
const one = [[1],,,], h = [1,,2], blue = [one, [,[1],,], [,,,[1]], [,,[1],]],
yellow = [one, [h, h, h, h], [[-1,,2], h, h, h]], red = [one, [[,1,2], h, [-1,1,2],]];
for (let i = 2; i < 5; i++) red[i] = quatmul(red[i-1], red[1]);
return [...(function **() {
for (let b of blue) for (let r of red) for (let y of yellow)
yield quatnormalize(quatmul(b, quatmul(y, r))); })()];
}
otherwise I can do like Object.entries(blue).map(b => Object.entries(red).map(r => Object.entries(yellow).map(y => ...)), but that is kind of ugly
is there a difference in the output, or you're just refining the implementation?
oh, the output should be the same as,
icosahedral_quaternions_vzome = {
const
one = [[1],,,], h = [1,,2],
blue = [one, [,[1],,], [,,,[1]], [,,[1],]],
yellow = [one, [h, h, h, h], [[-1,,2], h, h, h]],
red = [one, [[,1,2], h, [-1,1,2],]];
for (let i = 2; i < 5; i++) red[i] = quatmul(red[i-1], red[1]);
const output = []; let b, r, y;
for (b of blue) for (r of red) for (y of yellow) {
const q = quatmul(b, quatmul(y, r));
output.push(scalarmul([grsign(q[0]) || grsign(q[1]) || 1], q));
}
return output;
}
I'm just trying to figure out how these javascript features work mostly
the vzome2 version does not have the scalarmul... not necessary?
icosahedral_quaternions_vzome = {
const one = [[1],,,], h = [1,,2], blue = [one, [,[1],,], [,,,[1]], [,,[1],]],
yellow = [one, [h, h, h, h], [[-1,,2], h, h, h]], red = [one, [[,1,2], h, [-1,1,2],]];
for (let i = 2; i < 5; i++) red[i] = quatmul(red[i-1], red[1]);
const output = []; let b, r, y;
for (b of blue) for (r of red) for (y of yellow)
output.push(quatnormalize(quatmul(b, quatmul(y, r))));
return output;
}
maybe make your shape normalizer match the contract for vZomeRenderer
I want to keep playing with my convertShapes, but just for learning
see the shapes as currently pre-converted on my page... tip, waist, etc. name changes
so I should take in whatever format vzome generates, and spit out a normalized version?
convertJson is already doing everything except the reorientation
if you like, rewrite convertJson in your own style, matching its input and output formats
output format is what you see in https://beta.observablehq.com/d/70a3c92c576398bb#icosahedral_solid_connectors_shapes
but hopefully by the end of today we can get a list of strut endpoints rendering as a model
note that we have it already... just not rendering correctly 😉
then we can start to e.g. transcribe some old Zome documents into new notebooks
gotta do some real-world stuff... will be in and out
resolveModel is the function we're after, it just doesn't work correctly yet
@Scott okay I'm making this function a bit flatter: ```function convertShapes(shapes) { const strutShapes = {}; for (const name of shapes.strutGeometries) { const shape = shapes.strutGeometries[name]; const proto = parseAlgebraicVector(shape.prototypeVector); const [orb, rotation, reflected, length] = orbit2(proto);
strutShapes[JSON.stringify(orb)] = {
color: name,
prototype: parseAlgebraicVector( shape.prototypeVector ),
vertices: shape.vertices.map(parseAlgebraicVector),
polygons: shape.polygons,
tip: shape.fullScaleVertices,
waist: shape.halfScaleVertices
};
}
const ballVertices = shapes.connectorShape.vertices.map(parseAlgebraicVector) const ballShape = Object.assign({}, shapes.connectorShape, {vertices: ballVertices});
return {name: shapes.name, ballShape: ballShape, strutShapes: strutShapes}; }```
now I can figure out more easily where to put the rotation
I've gone a bit "all in" on map and reduce... need to back away, yes
imperative code can be clearer when the functional code gets too deeply nested
also I did something that causes an infinite loop. should figure that one out 🙂
oh I think my problem was notdoing the parseAlgebraicVector thingy
I should figureout why my quattransform or whatever doesn't just error out on those
you made it disappear... it went where the blue one is
@Scott so I think the shapes might be right now (or maybe not)
it would be for my converted models, but for the testResolve it should work
as I mentioned earlier, with shapes now canonical, I'll need to update my convertVSON to normalize, too
yes, the length is almost certainly wrong, since it was wrong before
try scaling up 3 powers of phi, to see if it renders
uh... what's this part about? orientation: model.quaternions.length,
it is not beautiful code, I'm sure you can find many improvements
huh, I thought I handled "reflected", but I don't see it
should work to do start: model.vertices.length + reflected
right, they are missing the normalization that we did for convertShapes
@Scott maybe you can help me out. I'm not exactly sure where I should be looking
does it matter that in the 'symmetry' object the orbits are indexed by color name?
maybe it was trying to look that up instead of looking up the orbit coordinates?
while I was at it I flattened that one too: ```function convertSymmetry(s) { const matrices = s.symmetry.matrices.map(m => m.map(parseAlgebraicVector)); const symmetry = Object.assign({}, s.symmetry, {matrices: matrices});
const orbits = {}; for (const orbit of s.orbits) { const prototype = parseAlgebraicVector(orbit.prototype); const canonical = JSON.stringify(orbit2(prototype)[0]); orbits[orbit.name] = { canonical: canonical, unitLength: parseAlgebraicNumber(orbit.unitLength), prototype: prototype, canonicalize: orbit.canonicalize }; }
return Object.assign({}, s, {symmetry: symmetry, orbits: orbits}); }```
@Scott this yellow strut is hilarious https://beta.observablehq.com/d/b30f26a5f6143d40
the converted symmetry object has orbits indexed canonically
speaking of flattening, there is no reason that symmetry needs a nested symmetry object... that is an artifact of the export
I left symmetry indexed by name on purpose, because it is only used in convertVSON
we need to look up canonical orbit names in convertVSON, so we need it indexed by name
@jrus why do you use the syntax "convertSymmetry = function convertSymmetry(..."?
just the function will bind as the cell name, without the "convertSymmetry ="
I like it because it makes the name the first thing in the cell text
so doing it this way keeps me consistent vs. other parts of my code
I'm still not sure what the deal is with the geometry being all broken. I should be doing deliberate debugging instead of just fiddling with unnecessary nonsense
I'm moving toward that... syncing with your convert functions
@Scott I sort of know what I have to do to debug this, but I'm procrastinating. probably because it seems like it's going to take more mental energy
the thing to do is (1) look at the vertices of some strut in a weird orbit (2) figure out where they should be for a reoriented strut (i.e. in canonical triangle)
I could never do the equivalent vZome debugging without my handy dodec key. Yes, it takes focus and energy
I'm going to start with just rendering the strut shape for a few orbits
I think I should take bits of your code and put them in new cells without as much indirection
as it is, if I change one cell, that thing is a function that gets called somewhere else
they still take the first half of the height of the page, but anyway :)
I made you a
THREEVector3 = function THREEVector3([x,y,z]) {
return new THREE.Vector3(grfloat(x), grfloat(y), grfloat(z));
}
function
which doesn't use any higher-order thingies, because I'm not sure how often you might call it
though extra function calls probably aren't going to be the bottleneck ever
it could also be
THREEVector3 = function THREEVector3(v) {
return new THREE.Vector3(...v.map(grfloat)); }
Paul Hildebrandt is in the hospital with a broken hip
I know some 20-somthings who broke hips skiing or the like... it can certainly happen
he just regained control of Zometool last month... stockholder's meeting elected a new board, and somehow voted Carlos out of the presidency
I think they both did some iffy moves, it escalated, they lawyered-up, ...
they have kept it going though, which is pretty impressive
too bad schools don't have more budget for materials (except "high technology" nonsense)
a lot of high school math classes would benefit from large amounts of available zometools, among other construction materials
yes. Even so, I think Zometool is a bit of a time drain... too much play, hard to fit into a lesson plan
yes. I think i've learned more math writing the code than using Zometool. 😉
I think kids will learn more from doing nothing but building zome shapes for a month than whatever else they were planning to do :)
it certainly is more engaging than symbols on a page
@Scott this thing is going to pick somewhat funny ones for edges/corners
as long as they render OK, it doesn't bother me... if we had some other code we wanted to consume it, maybe I'd care more.
if we super cared we could pick our favorite quaternion for each aliased direction and treat each corner and each side separately
@Scott https://beta.observablehq.com/@jrus/zome-arithmetic#normalized_blue_vertices
@Scott so we end up having the vertices in a different order here e.g.
@Scott does having the geometry inside out have ill effects?
I don't think that could explain the oddness with the yellow strut though
since we only need to canonicalize once for the built-in shapes, we could also just explicitly put what rotation to do
@Scott ok I think that flipping the blue strut inside out made it disappear
if you have a ball, the blue strut is probably inside. My blue strut did that
if I get rid of my normalization code https://beta.observablehq.com/d/b30f26a5f6143d40
handcrafted = { return { vertices: [[[0,0,1],[0,0,1],[0,0,1]]], quaternions: [[[1,0,1],[0,0,1],[0,0,1],[0,0,1]]], balls: [ 0 ], struts: [ { start: 0, orbit: "[[0,0,1],[0,0,1]]", orientation: 0, length: [1] } ] }; }
try rendering that... should get a blue strut inside a ball... then remove the ball
the blue is right, of course... normalization should have no effect on blue, which already has an x-axis prototype
so we should maybe just add a manual entry with the orientation # (or quaternion) to use to rotate a strut around to the standard orbit
remember, it is an index according to the vZome numbering of quats
I had started to do that normalization, but got distracted
@Scott it's also relative to whatever vzome considers the standard orientation
canonicalize should mean "how to point this orbit prototype to the vZome canonical triangle"
yao and simon went to the park.I was going to nap but I'll stay up a while more
feel free to experiment, we just need to have some control over what version is the one we both keep working on
switch to the scott channel, and click on the phone icon
@Scott apparently two of my safari tabs were taking all my cpu
@Scott it's possible that was what was causing the slowdown before
I think I could have been stuck on it for quite a bit longer
I'm going to investigate the other got-the-wrong-rotation issues
the rotation thing was because we were multiplying by the conjugate of what we wanted
we had the rotation to go from standard triangle -> prototype direction, but we wanted the other way
we were actually properly using the conjugate in changing the vertices of the model
Now I just need to understand it all... And we need to write about it
@Scott I'm glad to have a 3d-rendered interesting bunch of shapes to play with 3d mouse/multitouch with
I think we can do some useful research into better rotation methods
it's nice being able to experiment easily, e.g. draw all the purples
testResolve = {
const v0 = [[1, 1], [1, 0], [0, 0]]; // purple
const vs = icosahedral_quaternions_vzome.map(q => quattransform(q, v0))
return resolveModel(vs.map(v => [[[0],[0],[0]], v]));
}
@Scott okay yao sent you a video of simon saying 'icosahedron'
you could use the non-vzome quaternions equally well, right?
these are just hand-crafted... I'll export the vZome colors, and you can tune them for me
I know they are hand-picked. I just don't like the choices :)
I really need to make a good observable notebook color picker
no, I mean these in the notebook are just wild-ass guessed by me
so I can make some white struts in the blue direction etc.
one thing I want to make in observable is a "color list view"
that exposes an object like {red: 'foo', blue: 'bar', ...} to other cells, but shows itself as a list of color swatches next to a color picker
but aren't there a number of really nice color pickers online?
then you could drop that in, and someone could change all the colors for the orbits in their own notebook
the harvard computer society apparently clobbered my website
they'll probably re-enable if you ask... have seen this happen to other academics, even professors
I was going to link you to the color picker I had been working on a few years ago (just for a few days, so not very finished)
you may still have ssh access, even if the site is no longer hosted
that's a bummer, as that thing was still more useful than alternatives
I haven't changed anything https://beta.observablehq.com/d/b30f26a5f6143d40
need to apply a little strategy about how to factor these notebooks going forward
feel free to put your whitespace style back if you like :)
oh, I didn't notice the change... and don't know where to set it
oh, I mean, I removed all sorts of whitespace from your code
never understood... we need whitespace to read language, why shouldn't it help when reading code?
I just reflexively put things in a moderately standard-ish form (though I have never been good about camelCase names, even though everyone does it in JS)
given your emphasis on perception, you were my last hope
everyone else's code always looks like those old illuminated manuscripts with out ANY word breaks
if it were up to me punctuation would be a bit deemphasized
that's another thing... I hate reading black code, don't know how anyone does it
and I like identifiers colored (by semantic category)... but so many tools highlight keywords, the noise in the signal
anyway, if you want to change the space to whatever you prefer, go ahead
probably when I start trying to read some of your code, I'll have to
hey, is there any mechanism to give a notebook a TOC?
the usual idea, based on heading sizes in markdown/html?
Is it OK with you if my fork of your fork of my notebook is the one we take forward? I'll probably split out the non-canonical stuff (vZome specific) as a separate notebook
and the canonical stuff we can publish as "Observable vZome", or something in that vein
was having some "red" trouble with sharing... checking
observable storesyour changes locally in the browser so you shouldn't lose data from reloads
I'm factoring out all the vZome-specific into a fork
it might even be possible to generate some of the geometry from code. especially e.g. lifelike balls and the connectors on the ends of struts
sure, we want to be agnostic to how the shapes are produced... we can have a "super resolve" that creates shapes on the fly
I just think it might be nice to have a self-contained thing that doesn't depend on gists at some point
if you want to draw struts which go all the way to the center of the ball what would you do? just add 3–5 extra polygons at each end?
e.g. if you want to show how the lengths scale like the golden ratio
sure, just a nice tapered pyramid... sharp enough so RYB don't intersect (i.e. use the ball vertices)
David and I have used Trello for what little planning we do... mostly just a clearinghouse for bugs and ideas and todos
yeah, probably not without creating an account... lemme see if I can open it up somehow
did you create already? Trello acted like it knew you
Board not found.
This board may be private. If someone gave you this link, they may need to invite you to one of their boards or teams.
there is a todo mechanism in Slack, but I think it is only per user
@Scott so one thing I would like to do is make a version of the renderer which (a) has a transparent or white background, (b) can be stuck next to a block of text
I kinda like the idea of getting a bit more refined before we publish the notebook... more prose at least, but also some features
is there a way to put a canvas element in Markdown? guess it can always be HTML
probably the only current problem is that it always uses "width"
very good... I'll add pithy summaries, and move the description inside
even David and I don't use it much... we could also switch to Google Notes, which I use heavily at work and home
my head is not so good... I lose track, drop priorities
yeah, my projects stall for 5 years, and then I'm like "what was I trying to do with this one again?"
hmmm... virtual bobs is an interesting idea... I usually just want to use a 2x strut length or whatever
making a model physically realizable (validation) is something I've always wanted for vZome
I would sometimes want to demonstrate what can be done with physical models
I think it is much easier to experiment and innovate in Observable and Javascript... we may easily reach things vZome has never been able to.
also, please add your thoughts to https://trello.com/c/WQD9Wefg
personally I like passing objects instead of this chain API style, but it seems to be popular in javascript, so I won't object
we have a lot of potential parameters, or config models (like camera, lighting, background... )
we could almost just expose the 3js classes... one could even add a mesh, put their own labels on a model, etc.
the main value we are adding is to generate a group of meshes with orbit-based geometry
what is now the vZomeRenderer could really just be a convenience
I think start by just hacking together whatever you need for immediate goals
can worry about what a good API is once there are some use cases
I wonder if it takes extra gpu to render a big empty white canvas
@Scott I would actually like to render models in big views behind my text, and allow the reader to zoom in with the text getting overlapped if they desire
needs the ability to make the camera center not near the image center
the text overlay is a question for any canvas, not just 3js
it's more a question of getting the details of mouse control etc. right
@Scott I think you actually forked a slightly old version of my notebook
@Scott oh, I think I broke the vson models, by changing the prototypes / strut geometry
need to add a quaternion multiplication & scaling to get vson to render
@Scott is the right place to look for the colors defaultPrefs.properties?
```# These defaults only operate for vzome-core when not overridden by vzome-desktop.
color.red = 175,0,0 color.yellow = 240,160,0 color.blue = 0,118,149 color.green = 0,141,54 color.orange = 220,76,0 color.purple = 108,0,198 color.black = 30,30,30 color.white = 225,225,225 color.olive = 100,113,0 color.lavender = 175,135,255 color.maroon = 117,0,50 color.rose = 255,51,143 color.navy = 0,0,153 color.brown = 107,53,26 color.apple = 116,195,0 color.sand = 154,117,74 color.turquoise = 18,205,148 color.coral = 255,126,106 color.sulfur = 230,245,62 color.cinnamon = 136,37,0 color.spruce = 18,73,48
color.magenta = 255,41,183
color.snubPentagon = 187,57,48 color.snubTriangle = 87,188,48 color.snubDiagonal = 228,225,199
color.slate = 108, 126, 142 color.mauve = 137, 104, 112 color.ivory = 228,225,199
color.panels = 225,225,225
color.background = 175,200,220
color.highlight = 195,195,195 color.highlight.mac = 153,255,0
color.light.directional.1 = 235, 235, 228 color.light.directional.2 = 228, 228, 235 color.light.directional.3 = 30, 30, 30
color.light.ambient = 41, 41, 41```
@Scott so slate, mauve, ivory are for a different symmetry system or so?
I think for the snub dodec field... Not used, to within epsilon
@Scott btw, I think you did a pretty good job picking the zometool strut colors
I think the blue one is slightly greener than the physical zome struts
if I were choosing for zometool I would also use a slightly bluer green than the one they chose :)
so if you look at blue struts from 20 years ago they will look greener
at least, if they got sunlight instead of sitting in a box
Mine were a bit green when I bought them... Around 1999
the original green strut color was also yuckier I believe
or maybe not. let me see if I can find some very old ones
I think I'm misremembering. 'yuckier' is not the right description
Maybe Paul would enjoy a demo when he's back home recuperating
you should switch white to something that isn’t 255,255,255 I think
being so close to the top end means you lose some contrast in your shading
I'm open. I did try less saturation at some point, but I wasn't happy with it. I may have overshot
It feels like the shader still manages bright specular reflections, but I have seen washout, too
Mostly I get into trouble with too many bright lights, I think
@Scott Here's some playing with the colors. On the left, originals on top, new ones on bottom. On the right, grouped-ish
but I moved some of the very similar ones apart, and toned downthe extreme radioactive ones
Seems like there are too many still... I cannot name them all. Probably more in the preferences file than are actually used
Looks like I was wrong about the snubs... Those are the three I could not name
You can ignore anything after spruce... Anything else could get assigned brand new, distinct colors, and nobody would notice
we can totally change the snub ones if you want, doesn't matter to me
I don't know where it is used if anywhere... Ignore it
I think it was only there for defining cut lines for stickers, where the aesthetic is not the requirement... They match on the exact RGB
@Scott so for white I'm doing 238,241,247. worth testing to see if that looks dull
Don't really care... The physical balls are wildly variable. Looks like a rainbow if you get a bunch in a box, from different purchases
I tend to a warmer White point on displays, but let me look at it. Bluer will be nice for a default sky blue background
@Scott I should also add the zometool half-green color ("teal"?)
obviously for many purposes those can just be colored green
I never use it in vZome... All green lines are green regardless of what weird length they might be
I really need a tool where I can easily plot the hue/lightness of a set of colors as a scatterplot
hopefully maroon, red, orange, cinnamon, and brown are all now more easily distinguishable
that is, the color I picked might not really match the name
simon didn't want to go out... he wantedto keep playing with toys
@Scott sulfur, white, and ivory are the only colors lighter than the background. do you want sulfur way up there?
Background can get lighter. You want sulfur dingier? Sure. More important to be distinct from yellow.
I still have coral and rose too close together for my taste
Are you removing lightness from my set of variables for distinguishing orbits?
They are not at all cost for me. It is Rose not pink for a reason
oh, yeah, ideally shouldn't have colors too close to the background. I can make sulfur a bit lighter again
For all colors of course, but that one in particular they care about
your "rose" color looked like "hot pink" or "fuschia" to me. I was moving it toward a pinker kind of rose color
the color name "rose" doesn't have a super tight definition, since roses come in a wide variety of colors
And in observable, we can expect notebook authors to change it
yes, this is one reason I wanted to have lighter black, darker sulfur/white/ivory
so if someone uses pure white or pure black background there is still contrast
@Scott if there are some important constraints on the colors based on their meaning, I can try to respect those
my approach so far has been to try to make nearby colors as distinguishable as possible without fundamentally changing them
I wouldn't worry much... Though I have avoided the missing powder blue for years 😄
Sorry can't keep up with your keyboard... Swipe on my phone
so I am e.g. making blue darker, green lighter brown lighter, maroon purpler,
the goal is for (a) if you see a strut by itself you can guess what it is, and (b) if you see two struts on top of each other they don't blend together
on the right, are the chip placements mathematical in the middle?
generally, this is an improvement. I think Zome blue has gotten too dark, however... too close to navy.
cinnamon has lost its character... a little darker... halfway to its old level?
I like coral with a little more orange... distance from rose
please discard everything after spruce... it is a distraction and confusion
@jrus I refactored my notebook, splitting it. Published https://beta.observablehq.com/@vorth/wip-importing-vzome-models
and reshared https://beta.observablehq.com/d/25bd296a94f5834d
forked and deleted, now shared again as https://beta.observablehq.com/d/03d72a1e3a89df69
I suppose so, since the event stream is so full of garbage
when they allow unlisted publish, presumably everyone will start paying more attention
@Scott the issue with cinnamon was that it was very similar to red and brown and maroon
I tried to spread out the lightnesses of colors of nearby hues
it really doesn't matter very much... I'm sure nobody is too attached to it
I might have fiddled a bit more since yousaw. but I have maroon L* = 29, brown L* = 36, red L* = 40, Cinnamon L* = 51, Orange L** = 59
brown is really an odd duck in icosahedral, so I actually care less about it
it is a carry-over from octahedral, where there is no cinnamon to conflict
I also extracted createModelGroup, the core value... leaving vZomeRenderer as 99% pure 3js
back to tiny bandwidth swipey keyboard, or none at all
what direction did you want to move cinnamon then? just darker?
I don't think your original color had much to do with the color of cinnamon sticks, and a web search for "cinnamon color" turns up a very wide range
Pretty sure I picked the color, then thought of a name
I can't figure out the criterion for when slack will show images inline
@Scott you were saying there was something important about the choice of color for "rose"?
you were wondering about blue being made darker? After some more fiddling (lightened the background a touch) it's up so background L* = 81, yellow L* = 75, green L* = 53, red L* = 42, blue L** = 38. I think that will be pretty good contrast for the core built-in colors
I'm starting to really hate cinnamon... just too close. I think we could just darken brown (never used!) and get some room for cinnamon
blue still seems too dark to me... pretty far from Zometool blue, I think. I'd like to stay a bit more true for those four.
@Scott try this: ```color.red = 193,21,46 color.yellow = 240,169,1 color.blue = 6,96,141 color.green = 14,146,55 color.orange = 231,101,0 color.purple = 134,59,163 color.black = 48,48,51 color.white = 238,241,247 color.olive = 115,104,16 color.lavender = 144,127,227 color.maroon = 131,17,71 color.rose = 252,106,171 color.navy = 52,48,136 color.brown = 176,93,26 color.apple = 123,185,40 color.sand = 187,144,85 color.turquoise = 38,168,146 color.coral = 254,140,139 color.sulfur = 211,224,60 color.cinnamon = 146,41,22 color.spruce = 12,84,55
color.magenta = 219,41,118
color.snubPentagon = 170,27,111 color.snubTriangle = 153,158,0 color.snubDiagonal = 229,221,185
color.slate = 102,126,153 color.mauve = 146,82,131 color.ivory = 229,221,185```
@Scott we can certainly make blue lighter. I can try to match what a zometool strut looks like after 3d engine has added lighting etc.
@Scott do you have any favorite models that use many orbits?
make a yellow strut, select it, then make the 120 cell with the "polytopes..." command
yellow got too close to sulfur, I think... lost a bit too much orange
brown is the worst... just darken it, I don't care about balance there
I'm warming up to the red... now nicely distinct from orange, if not quite the real Zome red
highlights get a bit toward the magenta side, but I think it is OK
we don't have to agree, remember... feel free to keep a set where you like them.
I'll try to look at physical zome struts next to the one on screen and see how they compare
I wish I knew the scales so I could give you more precise direction... or I wish we could do this interactively
we could do it interactively in the browser version. not sure how close the lighting model is
yes, it helps to rotate a model... but not strictly necessary
I think we could go a little darker on black and still have room... lighting takes care of contrast with a black background
does it look bad? or you just really want a jet black color?
to be honest just toning down a few of the most radioactive colors and separating the brownish ones will get most of the way to where I wanted to go :)
not quite jet, but say 1/3 the way from where you are to full black
did you want to try a light cinnamon? or you just want them to look the same?
your maroon I would call too purple-y for the name, but I like the distinctiveness
usually a dark red on the redder side gets called like "burgundy" or "crimson"
for brown, a bit darker, and a bit more to the green side... very dark yellow
it's why people should use Munsell coordinates instead of random words
like black, it is rarely important... even less common, in fact
I can see the change, but not enough... darker please
what's the general concept behind the "cinnamon" and "brown" orbits?
brown arises very naturally in octahedral symmetry... it almost never appears naturally here, but I wanted a model flipped from octahedral to look right
all of brown, cinnamon, maroon, and sand seem to be roughly along a line, and all the same color
cinnamon is not very common... it is a triple intersection, but perhaps between three secondary circles, I don't remember. It shows up in odd linear transformations only
what if cinnamon goes back to being a lighter reddish brown then?
I think I added it only because I wanted to see if many previously "white" directions were actually this triple intersection
I like cinnamon where it is... light enough already.
yes, let's make brown more different, leave cinnamon stationary
I'd like to stop tuning for now, and work on my VSON conversion
we need more interactivity to wrap up the tuning quickly anyway
bear in mind, in spite of my nitpicking, I'm really happy with the balance you've already brought in
okay I've moved a bunch around. I'll try to get another set of rgb coordinates in a bit
yes, just a little. It has good spacing with lavender, and I know we'll lose a little
do you have a newer version of convertShapes, without the wtf logging?
```color.red = 197,36,49 color.yellow = 240,169,1 color.blue = 6,96,141 color.green = 8,141,50 color.orange = 231,101,0 color.purple = 122,66,171 color.black = 40,40,42 color.white = 238,241,247 color.olive = 116,104,1 color.lavender = 144,127,227 color.maroon = 131,17,71 color.rose = 252,106,171 color.navy = 52,48,136 color.brown = 105,54,1 color.apple = 129,191,30 color.sand = 187,144,85 color.turquoise = 47,174,151 color.coral = 254,140,139 color.sulfur = 196,231,81 color.cinnamon = 144,56,14 color.spruce = 12,84,55
color.magenta = 219,41,118
color.snubPentagon = 170,27,111 color.snubTriangle = 153,158,0 color.snubDiagonal = 229,221,185
color.slate = 102,126,153 color.mauve = 146,82,131 color.ivory = 229,221,185```
@Scott yes, did you look at my notebook? I think I published the one without that after you forked but before you did anything with your fork :)
I just commented those lines in convertShapes, but feel free to remove them
scott, okay, I made a vZome.preferences file and am looking there
red definitely needs to be oranger, but I don't think it's too light per se
orange can be a bit redder and darker to keep ahead of yellow
green can be a bit darker and bluer, turquoise can maybe be a bit lighter again to keep ahead of green
yes, and the lighting is something I need to fix, too... it should really change when toggling the outlines
I'm pretty sure I have white lights... check the default prefs file
no, directional.1 and directional.2 are not white... balance them and try again
would be nice if we could compare monitors... I'm pretty happy with orange vs yellow
we may be inside the range of variability, whitepoint, etc.
I think the colored lights could be causing your difference w.r.t. Illustrator
copy them from the prefs defaults you found, and adjust to balance, lighten, as you see fit
I don't know about the model view, but I think the direction config view is definitely improved by the color change :)
looks like one is slightly blue, one slightly yellow
that is the orbit triangle, making me happy as I said
@Scott I was trying to remove the prefs and re-load, but I think I might need to explicitly put the old colors there
if you remove the prefs, the internal defaults will apply on restart
it seems like it is still using my colors from previously
closing the last window quits vZome, on the Mac... but use cmd-Q to be sure
make sure you're not running two vZome instances somehow
@Scott I can't seem to get the "direction configuration" view toshow your original radioactive colors whatever I do
would the model somehow have the colors get saved with it?
I think you can also just delete the directions part of the XML... we don't have anything automatic here
it is a bad design, but it lets me manually adjust automatic colors once in a while
in the web view, the blue still seems too dark to me
maybe want to adjust the lighting on the web version to be comparable
yes, we could try, but remember we'd have to make it track the camera... bit of work there
we'll want to do it eventually for the "default vZome-y view"
```color.red = 197,3,20 color.yellow = 245,163,1 color.blue = 6,96,141 color.green = 2,130,53 color.orange = 236,89,1 color.purple = 122,66,171 color.black = 40,40,42 color.white = 238,241,247 color.olive = 116,104,1 color.lavender = 144,127,227 color.maroon = 131,17,71 color.rose = 252,106,171 color.navy = 52,48,136 color.brown = 105,54,1 color.apple = 129,191,30 color.sand = 187,144,85 color.turquoise = 47,174,151 color.coral = 254,140,139 color.sulfur = 196,231,81 color.cinnamon = 144,56,14 color.spruce = 12,84,55
color.magenta = 219,41,118
color.snubPentagon = 170,27,111 color.snubTriangle = 153,158,0 color.snubDiagonal = 229,221,185
color.slate = 102,126,153 color.mauve = 146,82,131 color.ivory = 229,221,185
color.background = 178,205,230 ```
I'm going to darken black a bit more and move turquoise bluer and lighter
I see what you mean about maroon looking more like "wine" color under this lighting
```color.red = 197,3,20 color.yellow = 245,163,1 color.blue = 6,96,141 color.green = 2,130,53 color.orange = 236,89,1 color.purple = 122,66,171 color.black = 36,35,38 color.white = 238,241,247 color.olive = 116,104,1 color.lavender = 144,127,227 color.maroon = 131,17,71 color.rose = 252,106,171 color.navy = 52,48,136 color.brown = 105,54,1 color.apple = 129,191,30 color.sand = 187,144,85 color.turquoise = 53,181,179 color.coral = 254,140,139 color.sulfur = 196,231,81 color.cinnamon = 144,56,14 color.spruce = 12,84,55
color.magenta = 219,41,118
color.snubPentagon = 170,27,111 color.snubTriangle = 153,158,0 color.snubDiagonal = 229,221,185
color.slate = 102,126,153 color.mauve = 146,82,131 color.ivory = 229,221,185
color.background = 178,205,230 ```
@Scott I think these white balls are fine. end up with more shading vs. ffffff
I made sulfur too green here vs. apple. since yellow went a bit orangeward we can send sulfur more toward yellow
purple: I think you maybe added a trace of blue? could use another trace I think
orange: did you darken and redden? I think we've lost a bit of distinction with red
they seem very distinct over here on the bit I just pasted
this may come down to monitors and where we have our whitepoints set
@Scott I can certainly believe that your display is far from sRGB
I don't know how to make my monitor sRGB... you can show me sometime
honestly, right now, I could accept everything except sulfur
@Scott ```color.red = 197,3,20 color.yellow = 245,163,1 color.blue = 6,96,141 color.green = 2,130,53 color.orange = 236,89,1 color.purple = 122,66,171 color.black = 36,35,38 color.white = 238,241,247 color.olive = 116,104,1 color.lavender = 144,127,227 color.maroon = 141,2,41 color.rose = 252,106,171 color.navy = 52,48,136 color.brown = 105,54,1 color.apple = 129,191,30 color.sand = 187,144,85 color.turquoise = 53,181,179 color.coral = 254,124,123 color.sulfur = 218,225,83 color.cinnamon = 144,56,14 color.spruce = 12,84,55
color.magenta = 219,41,118
color.snubPentagon = 170,27,111 color.snubTriangle = 153,158,0 color.snubDiagonal = 229,221,185
color.slate = 102,126,153 color.mauve = 146,82,131 color.ivory = 229,221,185
color.background = 178,205,230 ```
as you suggested, swing the hue back from green a bit
@Scott I think I was still tweaking something, but had to help make a bed
```color.red = 197,3,20 color.yellow = 245,163,1 color.blue = 6,96,141 color.green = 2,130,53 color.orange = 236,89,1 color.purple = 113,64,178 color.black = 36,35,38 color.white = 238,241,247 color.olive = 116,104,1 color.lavender = 144,127,227 color.maroon = 141,2,41 color.rose = 251,94,160 color.navy = 52,48,136 color.brown = 105,54,1 color.apple = 129,191,30 color.sand = 187,144,85 color.turquoise = 53,181,179 color.coral = 254,124,123 color.sulfur = 218,225,83 color.cinnamon = 144,56,14 color.spruce = 12,84,55
color.magenta = 219,41,118
color.snubPentagon = 170,27,111 color.snubTriangle = 153,158,0 color.snubDiagonal = 229,221,185
color.slate = 102,126,153 color.mauve = 146,82,131 color.ivory = 229,221,185
color.background = 178,205,230 ```
@Scott see if there's anything on that one you still think needs some movement
I think we ended up toning down my changes vs what I did originally, so that these colors are reasonably close to your original intent while being a bit more distinct
I was just about to ask for another side-by-side... I feel like I've squashed your efforts
my color memory is fading... you could tell me you were just messing with me, these are the originals 😉
weird how different they look in the swatches vs the orbit triangle vs the lit models
yes, it is a nice improvement, mostly for turquoise and sulfur
Yes, I could try a finer outline circle in the orbit triangle
@Scott for colors more similar to illustrator can always flatten the lighting
color.light.directional.1 = 145, 145, 140
color.light.directional.2 = 140, 140, 150
color.light.directional.3 = 30, 30, 30
color.light.ambient = 100,100,10
'lifelike' balls definitely look better with more ambient light
@Scott vzome's rendering is so different between outlines on / off
I'm looking forward to when people's 3D rendering systems have material colors specified multi-spectrally, instead of rgb :)
@Scott it's definitely possible to write alternative shaders that can deal with this kind of thing
but nobody bothers because it would be e.g. twice as expensive
i.e. instead of 3 coordinates for light colors, surface colors, etc., use 6 or 8 coordinates
if working with "rgb" or "rgba" textures, just use 2 of them and combine the numbers. the gpu doesn't mandate what the numbers should mean
At the end of the day it outputs a fragment color, though, right?
@Scott oh if everyone wants to start making multispectral displays that would also be great
to be honest I'm somewhat surprised camera vendors haven't tried more already. If instead of just rggb filters in a bayer pattern, they did a more complicated pattern with more color filters, the demosaicing algorithm out the other end would be slightly more complicated but it would be much easier to get good output from strange lighting conditions
I assume the manufacturing would be a little harder, but I can't imagine it would be that much harder
cameras should also try to put their pixels in a non-square grid, and they'd avoid a lot of square aliasing artifacts at high frequencies
it's possible to buy multispectral computer displays but they are at least 10x more expensive than rgb ones
on the display side as well, especially for stuff like phone displays, I'm surprised there hasn't been more experimenting with additional primaries and non-square pixel grids. the pixel density is so high and variable across hardware that people need to pretty much resample every image that gets shown on screen anyway
Multispectral means just a luminance scalar per frequency?
so you get something closer to a full spectral power distribution / reflectance distribution
basically though, all of our displays and sensors don't actually record or output 3 primaries for each (sub)pixel; the little units that emit color are separate
in desktop LCD displays it is typically skinny rectangles RGB RGB RGB ...
in cameras it is typically a checkerboard RG RG RG RG on one row then GB GB GB GB on the next row
but there's no reason you couldn't have a whole bunch of different colors
@Scott you don't need to change the vzome colors any time soon. let's figure out what kind of lighting to use, then under that condition roughly match byrg to the physical zometool ones
@Scott is there a reason that "with outlines" and "without outlines" rendering in vzome has such different lighting?
I think the problem is that it is not different, or not enough. The outlines made me want brighter lighting... Washout is less relevant
We should set up a lighting scheme or two that we want to target... I think that needs a real keyboard or phone
@jrus I think we're handling scale wrong. To explain why I believe that, let me lead up to it.
First, I've concluded that it is silly for me to try to map VSON models with their structure intact... far simpler to just extract the line segments, and resolve the model.
So that's what I have done. Reshared: https://beta.observablehq.com/d/03d72a1e3a89df69
Notice that the scale of the model does not match the scale of the parts. Bear in mind that the vertices (ball centers) do not get scaled, and the conclusion is inescapable: our shapes are too small.
This is born out by the "all purples" model we were rendering... the struts are too long, and the balls too small.
@jrus OK scale issue resolve. convertShapes now scales up by [2,3].
resolveModel works nicely with VSON and with hand-crafted... if you remember to scale up a hand-crafted model
@jrus I went a little crazy with notebook refactoring.
https://beta.observablehq.com/@vorth/wip-importing-vzome-shapes
https://beta.observablehq.com/@vorth/wip-importing-vzome-models
@jrus TLDR: everything is working, refactored into three notebooks above.
@Scott I added a quick type check to the simplify3 function
I think my type check is going to limit us to 32-bit integers, so only up to powers of phi from –46 to 46 or whatever
head is still full of more ideas... I'll get them in Trello
was wrestling with the conflicting demands of having library code in Github and viewable code in Observable, though it will be a while before we're ready for Github
well as long as it's clear that one is just an explanation
@Scott for example in a library you might want to use some specialized data type, add a bunch of error checking, write explicit loops instead of using higher-order functions, or the like
in a notebook you can optimize for the code being short and/or readable
the original goal of my "zome arithmetic" notebook was to show how short and easy most of the arithmetic is to implement using standard javascript arrays and numbers
so that's why there's a naive implementation of "simplify" and "gcd" with code showing, and then a hidden cell with the caching ones
and I have a bunch of code which is more compressed than usual, with multiple statements per line, golden rationals written in the shortest way ([,1] instead of [0,1,1], etc.), trailing }s in short functions on the same line as the return, and so on
I want to save vertical space, more than write the most maintainable code
@Scott I think for a library for this kind of thing I might use typed arrays, for example
maybe figure out some kind of macro / metaprogramming stuff so I can write reasonable arithmetic without sacrificing performance
lack of operators in JS is terrible for doing kind of numerical code legibly
I just need to have this boundary in mind as I think about the abstractions to introduce later
julia would be great for "zome arithmetic" in a general context I think
you could probably just write your own basic arithmetic types and then just use existing code for everything else
@Scott if you get the chance sometime, check if importing my latest "zome arithmetic" notebook causes you any errors
@Scott when I added some error checks it made me realize that orbit2 wasn't sufficiently normalizing its inputs, so I also changed that one a bit
you are importing gcd in your notebooks, but not using it
also, you don't have to credit me for "most of the fresh ideas and clever, clear implementation"
@Scott well the 3js thing seems to get a big bogged down with 4000 balls and 4000 struts
the list of 4000 quaternions is definitely not necessary :)
the actual resolveSegments function seems to be plenty fast
I just took 6 red struts along different axes, and made a path permuting them every possible way
Perf testing on purpose? Or you just have a big model?
Phone dying. Rendering your model, or maybe even your code, might put me under.
@Scott it's also possible it's just that the rotation speed on the mouse control is set way way too slow
no, we are paying for the multiple instances... lack of deduplication
rendering 4000 meshes that overlap perhaps 120 unique meshes
<sigh> parent information night at school. catch you later
@Scott so the cool thing about this shape is that every white node has a different combination of 6 directions
I think the no-straight-through is true of the red 6-cube also
so you can figure out how many nodes there are by taking 2^6
because we have every combination of + or - for each of the 6 directions
so anyway, arguably the whole symmetry system of the icosahedron could be just a projection of relations in the 6-dimensional integer lattice
oh, right, I was thinking about configurations, not absolute zones... it is a 6-cube
so there are 12 balls on the outside with symmetric orientation of 6 struts... all the same configuration, but yes, each one is oriented differently
you should read what David Richter wrote about the relationship between Zome and the E8 lattice
I thought it was a Bridges paper, but I'm not sure any more
I think that is the only paper about the E8 relationship.
Funny, he has the same folded Coxeter diagram that David had in 2005
I'll try Dechant, but I'm afraid it will be over my head
you've seen this, right? http://homepages.wmich.edu/~drichter/zomeindex.htm
I made these ones http://homepages.wmich.edu/~drichter/zometriality.htm
and I have to mention my own paper with David: http://archive.bridgesmathart.org/2006/bridges2006-429.html
reminds me... I've agreed to present a short talk about some Zome math thing, at the Celebration of Mind at Stanford, late Oct
okay, ask me closer to the date. that's far out for me to plan :)
I'll be prepping, and I think I need to order more struts. All of mine seem to be in the ceiling
I got a whole bunch of "creator 3" kits on ebay for unusually cheap prices a few years ago
in general the kits other than the hyperdo one aren't really worth it
do you want me to tackle removing duplicates in resolveModel?
definitely within my reach, though my code won't be as elegant
if you want 1st generation extra long struts, https://www.ebay.com/itm/372431276457
hmmm... first gen were pretty crappy, too easy to pop out, sharp edges, etc.
they don't pop out that easily. they're pretty tight. mostly they are just "different"
very true... I traded some to Rick Sommer on that basis, to get some modern ones 😉
yeah $40 with free shipping is a good deal for that one. wonder how many snipe bids it will get
oh wow wtf why did this person have so many red struts in white? https://www.ebay.com/itm/223120514086
I wonder what art project needed a ton of red struts in white
I think architecture, in this case... RT-based zonohedral buildings
Paul was going to try to build housing at one point, I believe
I like the alternative colors for some explanatory purpose
I'm tempted by the old box kit, but mostly for the box!
I'm thinking I need to give some love to vZome itself for a night or three... get a new build out with some export/copy features for us.
@Scott and I definitely want to try to make new mouse handling
really, work on whatever is fun for you... leave me some low-hanging fruit 😉
every time the mouse leaves the frame where the rendered cells are, it stops spinning
but starts again if you cross another cell... pretty unpleasant
also you cannot right-click within a cell (like on an import link)... event is eaten
oh, it also eats my text selection efforts everywhere on the page
I consider that low-hanging fruit... just needs perseverance and doc reading, etc.
d3-drag gets a lot of stuff right that I wanted to do myself years ago
the hard part with GUIs is still (as ever) managing state
e.g. what is selected, what is being clicked and dragged, when should the selection change vs. objects get moved, etc.
should the mouse movement update objects in place, or keep record of the start of the movement and be ready to revert the whole action
I feel like web + mobile have caused a serious regression in all mouse + keyboard UI stuff
desktop apps today are typically worse at a lot of it vs. mac apps from 1995
figuring out how to handle all the edge cases in a reasonable way is still just as hard now as it was then
and there's a lot of half-broken defaults they fall back on
undo stack, state management... that's all above the level of events.
if I want to make e.g. a little tool for editing bezier curves
a lot of example code you'll see is just too ... example-y
there's a lot of possible choices to make about how the mouse interaction should work
doesn't lead you to a framework for thinking about it.
again, perhaps not obvious how to map to a geometric state, but I don't expect that to give you much trouble
I haven't actually seen a react app with a UI for doing "content creation" type workloads that I really liked
even if they don't, that is not an impossibility proof 😉
but most of the stuff seems geared toward "how can I made a web browser displaying a glorified email client bring a cutting edge computer to its knees"
more likely an inverse correlation between math programmers and software engineers, or something
model yours well, and your events well, and I don't see how you could fail
@Scott but e.g. I want to make a little categorical color picker so we can see hue/lightness of all the vzome colors
sure, but I don't know if it will help you build the next cool thingy very much
draw me a wireframe, talk me through it, and I'll happily try the React modeling
I gained a little bit of expertise with my vZome client/server thing... so I went beyond the simple form-like stuff
just for braggin'...: https://codepen.io/scottvorthmann/pen/xrVaPw
hmm, either it is broken, or I've forgotten how it works
did you ever make this 6-cube thingy? it's pretty interesting
yes, I made it with the triple (and double?) red bobs. Want me to send you a bunch?
good use for your 3-way-intersection bobs if you want to buy a whole lot of them
I don't plan to deconstruct or make it again anytime soon
though it did get a lot easier once I understood how the shape worked
the bobs are pretty forgiving, but I was worried (unnecessarily) about breaking them during deconstruction
I want to build one that is removing hidden cells... no overlapping cubes. May not be that interesting. The yellow-strut 10-cube is really only doable in that fashion, physically
so I think it is the case that every zome-representable point has a unique representation as a sum of 6 (half-)integer red directions
there is an embedded dodecahedron from which you can make a bunch of blue cubes
@Scott oh, I figured out one more reason I like putting foo = function foo(...) {...}
and then it ends up as foo = { ..... return function foo(...) {...} }
https://beta.observablehq.com/@vorth/wip-importing-vzome-models
red-quaternion projection of 120-cell, using copy.vson command
too big to paste in a cell (literally... it gets truncated), so pasted in a gist
too many steps, feels like... I'd like vZome to do OAuth login to Github, make the gist, and copy the cell Javascript to the clipboard
@Scott can we also prevent 3js from disappearing further away elements?
yes, I have been meaning to do that... simple change to renderer
("red-quaternion" means that I set three parts of a quaternion from a red strut)
I don't like their approach to zoom... I think they are just dollying the camera back and forth. vZome's is better... maybe does it by scaling, I don't remember
@Scott ```resolveSegments = {
class IndexedSet { constructor() { this.contents = []; this.map = new Map; } add(item) { const itemhash = JSON.stringify(item); let index = this.map.get(itemhash); if (!index) { index = this.contents.length; this.contents.push(item); this.map.set(itemhash, index); } return index; } }
return function resolveSegments( segments ) { const scale = [2,3,1]; // phi cubed const vertices = new IndexedSet, quaternions = new IndexedSet, balls = new IndexedSet, struts = new IndexedSet, strutdata = [];
for (let segment of segments) {
const startpoint = scalarmul(scale, segment[0]);
const endpoint = scalarmul(scale, segment[1]);
const startindex = vertices.add(startpoint);
const endindex = vertices.add(endpoint);
balls.add(startindex), balls.add(endindex);
struts.add([startindex,endindex].sort());
}
for (let [startindex, endindex] of struts.contents) {
const startpoint = vertices.contents[startindex];
const endpoint = vertices.contents[endindex];
const displacement = vectorsub(endpoint, startpoint);
const [orbit, quaternion, reflected, length] = orbit2(displacement);
const quatindex = quaternions.add(quaternion);
strutdata.push({
orbit: JSON.stringify(orbit), orientation: quatindex,
start: reflected ? endindex : startindex, length: length });
}
return {
vertices: vertices.contents, balls: balls.contents,
quaternions: quaternions.contents, struts: strutdata };
} }```
not quite sure why it has 65 balls instead of 64 to be honest
oh I know why. it's because I'm testing if !index instead of if index isn't null
@Scott that should perform pretty well under any typical use case
Good night. vZome regressions running. Will merge to master and build tomorrow
the slowest part is probably doing a JSON.stringify a whole bunch of times
it's not the ideal way to compare numerical data, but it should work okay
I take comfort knowing that Map.get is probably the most heavily optimized and most often executed code on the planet
also it is good to know that you will use the occasional class under extreme duress (four instances at once) 😉
I haven't really used the new type of javascript classes much
coffeescript (v1) had "classes" like this https://coffeescript.org/v1/#classes
look over at the right side to see the generated javascript
@Scott the alternative to using a class in our zome code would be to make a function which operates on an object with a map and list inside. not worth the trouble :)
Yeah, and all that mess is still there, just hidden and optimized
or at least that was the case a couple years ago. I'm sure they are working on it
one of the nice things about coffeescript was it compiled down to mostly bare for loops which are blazing
all the map and foreach and for-of loops and ... are slower
also it compiled destructuring down to separate lines, etc.
Check Trello I guess... Sorry in a meeting. I did a little prioritizing there. See what you think
I'm going to see if I can figure out the half-integer red-strut coordinate system
I think there are some interesting symmetry operations you can do by swapping / reflecting some of the red strut directions
@Scott given any shape, there are 6! 2⁶ different versions you can get that way. but obviously many of those are isometric in 3-space
I guess flipping all the signs at once also leaves the same shape
not sure if there are actually (potentially) 192 different versions or if it's less than that in the end
@Scott I really like these different integer coordinate systems. e.g. using 3 integers (even non-negative if you want) to represent points on an equilateral triangle grid
barycentric coordinates are relative to any arbitrary triangle in any affine space
in barycentric coordinates, the sum of the coordinates is always 1
@Scott interestingly, if you pick any set of 6 non-collinear (out of 12) red directions, and then starting from an arbitrary point you travel along every possible permutation of those vectors, you always make this same 6-cube shape (and you always end up reflected across the origin from where you started, along either a red or yellow direction)
every one of the 2^6 choices of signs has either 3-fold or 5-fold symmetry
there are 2 different types (with 20 each) with 3-fold symmetry, plus 2 different types (12 each) with 5-fold symmetry
I'm sorta curious what good literature there is out there about this kind of thing, that isn't too inaccessible. once you have code up for drawing these things, it should be possible to make a whole bunch of little exploratory tools
each can be its own notebook or whatever; there's a bit more freedom than a desktop app, because you don't need to worry about cluttering up the UI
uh oh. zometool website:
"Down for Maintenance
Sorry, we are not taking new orders at the moment. Our production equipment is currently down for upgrades and maintenance. Please check back soon. Thank you. - Zometool Inc"
@Scott I have one thing it might be useful to 3d print: "caps", that just extend a strut to the length of the middle of the node. i.e. half a bob, maybe with a closed end
They may be having a production tooling problem... But they would not shut down the website for that
Caps are an interesting idea... More satisfying and informative for a free strut end
Though that is the 1% use case... Usually you would want a ball if they meet
Heck, in 10 minutes I can model both options, and let the user decide
Oh I should have read further... It is the equipment. I remember seeing something on their slack channel
Both ball machines were down for a time in May. Sounds like one had been unreliable already. They may still be limping
I would be happy to throw them a few hundred bucks but I doubt it would solve anything serious
Around $8000 for a new CPU card was what they needed in May apparently
it's really too bad that schools spend so much money on computers etc., instead of other kinds of materials
@Scott huh, according to https://en.wikipedia.org/wiki/Quasicrystal sometimes the projected 6-cube (i.e. red strut coordinate system) is used for working with quasicrystals
That's pretty cool. Good luck finding something at a general level
I just physically made (a isometric representative of) all of the possible vectors producible from red struts
Closed under blue and red... So probably under Yellow too
at least, if you want to use 3 orthogonal blue directions + their phi-multiples
you can get any blue strut from integer multiples of unit reds
and then from there we already know how to get everything we wanted from half-blues
@Scott I wonder where a good place to start is .... https://scholar.google.com/scholar?cites=10544558457029523701
okay just bought this other super-old ebay zometool kit https://www.ebay.com/itm/372435621263?nordt=true&orig_cvip=true
@Scott the big question with a red-strut coordinate system is if we need 2 different types, should there be 3 of each, or 1 of one and 5 of the other.
it works out the same either way, but changes a bit the way to think about it
or I guess they can be 6 symmetrical ones thought of in an orthogonal blue coordinate system
most logical w/r/t a blue coordinate system is probably to have the directions (1, φ⁻, 0), (0, 1, φ⁻), (φ⁻, 0, 1) be one type, and the directions (1, –φ⁻, 0), (0, 1, –φ⁻), (–φ⁻, 0, 1) be the other type
I would place a bet on a good book from Marjorie Seneschal
I should figure out what the normalized orbit numbers look like under gnomonic projection onto a yellow plane, when we use barycentric coordinates with red struts at the corners (or similar)
would the barycentric coordinates necessarily be floats?
hey, want to talk about Observable vZome on Sept 29 at Stan's house?
@Scott I can show you a bunch of physical models if you want to video chat :P
@Scott oh, I realized I should probably remove struts with zero displacement in my code from yesterday
a zero somewhere we could recognize in resolveSegments
@Scott would be fun to test performance of making a super complicated model and finding enough info to render it in browser vs. vzome 😈
@Scott oh nice. orbit2 just spits out that 0,0 is a blue-orbit strut with zero length
You mean compare the performance of resolve in web vs vZome. Web would win. 😁. Question is, how much
```resolveSegments = { class IndexedSet { constructor() { this.contents = []; this.map = new Map; } add(item) { const itemhash = JSON.stringify(item); let index = this.map.get(itemhash); if (index == null) { index = this.contents.length; this.contents.push(item); this.map.set(itemhash, index); } return index; } }
return function resolveSegments( segments ) { const scale = [2,3,1]; // phi cubed const vertices = new IndexedSet, quaternions = new IndexedSet, balls = new IndexedSet, struts = new IndexedSet, strutdata = [];
for (let segment of segments) {
const startpoint = scalarmul(scale, segment[0]);
const endpoint = scalarmul(scale, segment[1]);
const startindex = vertices.add(startpoint);
const endindex = vertices.add(endpoint);
balls.add(startindex), balls.add(endindex);
if (startindex != endindex) struts.add([startindex,endindex].sort());
}
for (let [startindex, endindex] of struts.contents) {
const startpoint = vertices.contents[startindex];
const endpoint = vertices.contents[endindex];
const displacement = vectorsub(endpoint, startpoint);
const [orbit, quaternion, reflected, length] = orbit2(displacement);
const quatindex = quaternions.add(quaternion);
strutdata.push({
orbit: JSON.stringify(orbit), orientation: quatindex,
start: reflected ? endindex : startindex, length: length });
}
return {
vertices: vertices.contents, balls: balls.contents,
quaternions: quaternions.contents, struts: strutdata };
} }```
okay added a if (startindex != endindex) before adding a strut
that should handle it, since each vertex (by position) is only allowed once in the IndexedSet
@Scott it would be fun to have a "project the view from the current camera onto half a sphere" view
shit, I need to get my act together with doing better map reprojection in webgl
personally I think the right format for this is something based on the octahedron. can use a power-of-2 side square texture
which means to do antialiasing ideally you want to look in a hexagonal neighborhood
which means you don't want to just use naive existing resampling code
@Scott the first hangup here is that I want to figure out the right metric to judge distance errors on a map, and then optimize for that metric for a map from equilateral triangle <-> octant
all the existing metrics for judging map quality are subpar I think
if I can develop better criteria I can later use it to optimize maps of countries, continents, etc. etc.
but to start I want to have an octant <-> triangle map, as it is useful directly for several other projects
there are then 4 obvious choices of projections from octant -> triangle: gnomonic, conformal, distance-error-optimizing, and distance-error-optimizing while preserving area
that map lets me make a nice grid (and set of points, etc.) on the octant, so that I can optimize other maps from the octant -> flat paper
but also I can use it to define pixel positions for a raster image on the sphere
I see. Feel free to work on whatever floats your boat! We have good momentum; I'm not worried
@Scott I'm looking at this red projective thingy again. I need to figure out how it's related to https://en.wikipedia.org/wiki/Fano_plane
it seems like we have 2 fano planes nested with corners connected
the "sum 3 red struts" orbits that aren't yellow ones per se must be important
@Scott so lavender and olive are "alternate yellows" in the 6-cube coordinate system
which makes me sad that olive is a yucky type of color :P
So we could define a different coloring scheme... A good feature anyway
Those 3 seem to usually come together. I think of them as arising when we map some fivefold symmetry to octahedral
I'm not actually sure if adding the three red struts with unit weight is the right thing
what I actually want to do is connect lines between red corners and the blues at the centers of the opposite sides
we can represent both points and planes as 3-vectors, but potentially people can screw up and mix them
(points = homogeneous vectors, planes = homogeneous bivectors)
or maybe not normalized per se, but I mean 3-proportions representing a line pointed along a vector, 3-proportions representing a plane pointed along a bivector
In my attempts to implement GA, I have gotten frustrated. It always seemed it would be a good fit with algebraic fields, and geometry based on them.
implementing full multivectors is easy enough I guess. the tricky part is what UI to expose to users
I think I got discouraged when it became clear that the "magically intersect subspaces" operator turned out to require some special cases
I haven't even thought about what conformal GA looks like for zome
I've thought a little, and it is really intriguing to think about... but I get a bit lost
a sphere that is dense in S2, but has no continuity!
Calculus class really ruined my ability to comprehend that
well and a lot of points that can't really be projected onto a sphere
but the idea of using algebraic fields to model hyperbolic geometry... that would be amazing
part of the frustration with GA came from the disparities between the books
yes, I've seem some of that before, lots of his work with Roice
I'm not sure how GA will help you do projective geometry... but I don't think I'm ready to learn that tonight
@Scott projective geometry is just the geometry of proportions
i.e. a🅱️c where we can multiply all 3 by an arbitrary (non-zero) scalar without changing the point
@Scott usually it gets drawn by normalizing one of the coordinates to 1
@Scott but you could also e.g. normalize it so that the sum of squares is 1
@Scott anyway, you can often just work with the 3-proportions without normalizing them first
@Scott so to join 2 points is the same as joining 2 lines through the origin to get a plane
to intersect two projective lines is the same as intersecting 2 planes through the origin to get a line
(except it's better to remember that vectors and bivectors are distinct types of objects)
the last is my favorite... it is example-rich, and only 1/2 thick... heavy on pragmatics, light on theory, metrics, conformal model, etc.
hey, Doran and Lasenby has a chapter section on projective geometry... I'll read that
@Scott I really need to dive in and work a lot more problems
I feel like I'm still missing some key geometric insight in the conformal model
that one I loved at first... the first chapter offers a paradise, then the rest of it reveals all the ugly details and special cases
@Scott so I've been thinking about how to draw projections onto the plane / sphere
and then for "points" we can draw a section of a cone about the point (that would extend through the origin)
henry segerman (and many others) show the way the stereographic projection distorts size by making lines on the plane as if they came from uniform width lines on the sphere
but I think we can make something cooler looking by taking our flat rings (from above) and applying the same sphere inversion that would take the sphere to the plane
so they will end up extending further out of the plane where other people's lines would be thicker
3js let's you define an equiangular projection as your background image... Like what you see in sketchfab
okay. so a stereographic projection is a slice through a sphere inversion
so imagine we have the circle x^2 + y^2 = x, and we do a circle inversion through the unit circle
x^2 + y^2 = x is the "core circle" which has a diameter from (0,0) to (1,0)
so if you invert in the unit circle, the point at (0,0) goes to infinity
if you have arbitrary other points in the plane, you can do the same inversion on them
now imagine we draw little orthogonal tickmarks representing some points on the circle
when we invert those in the circle, we get tick marks (at various angles and lengths) across the line
not quite sure how to do a similar thing for gnomonic projection
can obviously also just draw lines in the plane with svg. which might be clearer for general-purpose use. but I think the 3d version may be helpful for getting the initial idea across
@Scott anyway, I think we can just make one prototype 3js geometry thingy for the "planes" intersecting the sphere
we can just make it a 200-sided polygon or something. not sure how many parts it needs to look plausibly circular
It is fun to make the walls disappear... initially you see a rhombus. Make a pentagon or triangle.
@Scott I don't quite get why they triangulate it that way
@Scott I wonder if some shadow cast from rings -> sphere would help with the disappearing walls
inverting arbitrary geometry in a sphere should be trivial there
I think just adding a great circle line for each ring might be nice
or just give the rings some thickness, maybe a slight taper
I don't know how to do shadows... lighting is easy to play with of course
we could also just do away with lighting... try it with MeshBasicMaterial
but then the width of the rings may get hard to understand
I think the lighting would be OK, if it stayed fixed... the trackball rotated the object not the camera
I think needs more ambient light, more light overall, more neutral lighting
can't be hard... the trackball should be doing nothing but setting a quaternion on its object, but it didn't work when I switched it from the camera to the mesh group
I'm not attached to this notebook... consider your fork to be the master
lights are attached to the camera, the camera is added to the scene
published again: https://beta.observablehq.com/@vorth/wip-observable-vzome
@Scott at long last yao is home (for the past 30m or so) and settled in with simon, so I can try to do something useful
and the fact that it sticks to the camera... so finally have the vZome approach
I have it slightly different in vZomeRenderer than in the ring demo... seemed too bright for the former
Odd, I always thought my approach in vZome was backwards... the hard way to rotate the object under the lights and camera. This is clearly the preferred approach in 3js
@jrus if you want to add some shape to the rings, you could play with LatheGeometry
these geom examples have a control panel you can toggle open to play with the params
@Scott one thing we could do is add a pale/translucent stripe on the sphere on either side of the wall
of course we can do our own Geom... just was looking for shortcuts
if you move the mouse some distance and then back the other way
the LatheGeometry is pretty close, with just a V-shaped profile input
I don't know if it's just dropping some mouse events or what
that could be still the listener going on and off as you cross canvases
I don't think debugging that is a good use of our time, honestly... better to wait until you have your d3-drag replacement
seems pretty smooth to me... could be your mouse? Your browser?
either from the device or even from pixels on retina screens
Chrome is way behind in some things, off on a limb on others
well, I mean the Chrome devs are just better... so fewer bugs
google just has different priorities than mozilla or apple
chrome is the biggest resource hog. won't even run under my typical workload
for the longest time chrome's svg and other graphics api implementations were buggy as hell
remember I have like a 10-yr old CPU... and I'm happy
when I was tracking the webGL support, they always seemed to lead.
@Scott anyway, that explains why I was super frustrated by the mouse stuff with TrackballControls; it's pretty bad in safari
I wasn't sure at first when I saw them, but I'm warming up to your ring walls
@Scott I think a 30% opacity perpendicular ring just above sphere surface would help
yes, and she doesn't sub out... one of the better passers/defenders/servers
no, not JV... not even really varsity, from what I've seen
i.e. the outside hitter goes across to the other side of the setter
that would be only for the premier clubs... 16, 17, 18
that's probably more a high school varsity / beyond kind of thing
lots of tricks for boys varsity, but the girls don't seem to have the tools, at least at this school
Derek's team last year won NorCals... there is no state championship
he didn't get to play much, there are 12 seniors (!), and now about 8 juniors too
I hope he plays, but he'd rather spend all his time on video games
I didn't play varsity my junior year because I broke my leg in the winter and wasn't really recovered
they only lost 5 seniors from last year, and only two were really starters
and then didn't play at all my senior year because was out of the country then busy
it is a good sport for life, I think... I played a lot of co-ed after grad school
but it was still fun, since nobody took it very seriously
I'm going to try to tackle the mouse listener problem with trackball
I considered trying to walk on to the team, but I didn't really want to spend that kind of time on it
it is the top two items on our "before publishing" list
I don't think it was that competitive at harvard, so I could have maybe gotten on
maybe I should try to find some pick-up games / non-serious league in the city at some point
OK, vZome 6.0 build 27 is available! http://vzome.com/home/index/vzome-60/
try the "copy.vson" command... remember the paste in Observable won't work for bigger models
fair warning: I meant to do this already but forgot... going to add you to the acknowledgements in the about
you've already had more significant and direct impact that some of those on the list
uh-oh... it should not be build 27, but build 37, I think
lmao @jrus... http://www.smbc-comics.com/
hey, can you generate axes for red and yellow planes quickly?
rather, quaternions that take the Z axis to those axes
given symmetries, if you have quaternions that take X they will work with Z
the easy thing is just re-orient a blue strut onto the strut you want by dividing the vectors, then normalize to unit length, then take the square root
to get square root of a unit vector, add to <1,0,0,0> and then divide by the square root of length
I think I would make the default plane perpendicular to the x axis
square root of a rotation is conveniently just done by finding the midline of a parallelogram. this is the "half-angle formulas" in trig
or not just square root, but halfway interpolation of any arbitrary quaternions
hrm... I was expecting this to be a one liner based on your existing notebook.
those combinations of rotation + scaling totally screw up the coordinates
oh, you can definitely make a floating point quaternion without issue
not 3 (that would be if you wanted to rotate and scale the other way)
now you find the magnitude of that thing (3) and divide by the square root
in other words, take (1 – 1/√3, 0, 1/√3, –1/√3) and divide by √[ 1 + 1/3 – 2/√3 + 1/3 + 1/3 ] = √[2 – 2/√3]
you get nested square roots, but the actual algorithm part is easy to write floating point code for
I only need one, I suppose... I can just apply the icosa quats to it
I think yao might take simon out tomorrow and give me a few hours of quiet time
@Scott 0.4597008433809830, 0, 0.6279630301995544, –0.6279630301995544,
@Scott wtf is this shit https://en.wikipedia.org/wiki/Rhombic_triacontahedron#/media/File:Rhombic_triacontahedron_box.stl
is it just my gpu or is this modeling thingy horribly broken?
@Scott I'm curious to hear your general thoughts about which planes are interesting
@Scott do you find use for all the planes perpendicular to any interesting strut direction?
or do you mostly construct planes via intersecting existing points?
e.g. the blue planes are just what you get if you start with 6 red points and intersect them
I haven't done very much with the planes. It might be interesting to drag out planes instead of struts. I just color any panels according to the orbit of their normals.
@Scott so there are two ways you might get new points. (1) take 4 points, find the line joining each pair, and then find the intersection
if you go with the second method, you can basically consider points and lines (or vectors and planes) to be the same type of object. just repeatedly use the "cross product" between pairs to generate new elements
you can freely combine a point and a line, a line and a line, or a point and a point without worrying about the type, because they have the same (3-proportion) representation
@Scott I'm trying to think about how we could draw something like that 'zigzag' picture for zome orbits
and also how we could automatically generate a large list of orbits and order them by some heuristic representing how basic they are, so we can color them using some automatic method of generating colors
@Scott basically I'm not sure how generally important the orbits (vectors) are that arise from directly taking the dual of a join of existing vectors
I think we pretty quickly get a bunch that you don't have names for
I'll have to understand this in terms of the icosahedron great circles/ planes. But not until later...
@jrus still trying to understand the "dual of a join of vectors".
dual of a join of vectors is just the cross product of the vectors
right, which is how the new orbits usually arise in Zome space... red X blue => orange
equivalently the planes orthogonal to red and blue intersect to form a line... which we call orange
join is outer product? I thought there was more to it
@Scott the point is just that vectors and bivectors are conceptually different types
@Scott so you can also get new orbits by taking the join of two vectors, then taking the meet of two resulting bivectors
now you can multiply them and take the vector part of that
make this concrete for me in terms of by red/blue/orange example
@Scott the point is just that if you do (a^bI)^cI = a x b x c, where I is the pseudoscalar, and ^ binds tighter than clifford product, then you are combining un-like objects / blurring the distinction between the types
you are treating "axial vectors" and "polar vectors" as the same
this is why I dislike the cross product, because it doesn't allow us to make those distinctions
@Scott but anyway, it's fine with me to consider duals to be the same color or whatever
yes, it is just the only way I learned how to think of these things. Every time I read GA I feel like I almost internalize it, but it always slips away without a visualization
it's likely you can get to all of these dual orbits by repeatedly doing joins and meets
I've done that for quaternions too, trying to get interesting projections from 4d
I want to try to take one of these methods, generate like 10k (or a million) orbits, and then draw them from least to most important, in the style I was drawing those zigzag pictures
you just want to remember their "generation number", at least for one measure of importance
draw both on the plane (gnomonic projection), on an orthographic or perspective projection of the sphere, and maybe stereographic projection
yeah, so make an ordered set thingy, and every time we get a new element just multiply against every previous one in our set, adding new products at the end
sounds great, so what is stopping us? You have part of GA already implemented
I bought a "ball of whacks" to go with my big 6-cube thing
so we can seed it with two blue vectors, one on axis and one off?
love those toys, I have all of them until he started doing multi-color
There is a cool tiling you can do... a planar arrangement with a triangle grid symmetry
where the peaks on one side match the dips on the other
like the IndexedSet I was using for dedupe in the shape renderer thingy
blue lines correspond to blue planes, but intersecting blue planes gives you red lines and yellow lines
joining red lines/vectors (red projective points) gives you blue planes/bivectors (blue projective lines). then intersecting blue planes gives you red/blue/yellow lines (projective points)
three blue lines should be enough to generate the rest of the blue lines as plane intersections, and the reds and yellows too
really you could start with red, yellow, or blue, and generate the others, right?
if you pick 3 orthogonal blue directions you don't get any 5-fold symmetries
yes, easy to miss the right yellow needed to go beyond octahedral
I think you might need 4 blue directions to start. not precisely sure
don't see why... it should correspond to three mirrors for the Coxeter group
well I'm thinking about which quaternions you need. not sure about join/meet of planes/lines
the trick is to treat all RYB lines as equally "important", whether they are the three you started with or not
anyway, it doesn't really matter exactly how many you need to start with
it's a bummer everything on george hart's site is vrml files
If I were doing this on my own, whenever I generated a "new" line, I would generate the whole orbit. Are you thinking of doing that, or working only in one fundamental region?
or you'll end up treating other zones as "next generation"
and the combinations with different versions of the same orbit are different
it's probably possible to be clever and cut down the amount of work involved
but just super overdoing it is fine too. computers are fast
in essence, you need to be sure you use all five orientations of green lines, for example
one way is to store one copy of the orbit in the indexed set
then take each one in turn, and try multiplying by every orientation of every item in the indexed set
or maybe: do two generations, and we'll render the result
I was just going to make an ordered list of the first 1000 or something
amazon reviews of high school textbooks are the least useful thing ever
then "resolve" a few to see where we leave the vZome realm, and perhaps what is missing there?
oh, right, I need to try your quaternion for yellow!
"good". "great". "arrived damaged". "terrible textbook", "item as described". "I bought this so my son could leave his book at school and have another copy at home". etc.
I think there are some people who just reflexively write reviews even if they have nothing to say
"Who wants this anyways? I bought this because my parents forced me to during summer vacation so that I wouldn't forget Geometry even though I am taking advanced computer science over the summer for college credit. You will not need to buy this book because usually you take geometry for ONE YEAR it is just stupid things, basically all algebra. If anything, buy an algebra textbook or something because all of the stuff in this book I all ready knew. So there is no need to buy it for your kid. On the other hand if you are an old parent or something and want to learn some basic math this would be a descent book to purchase, but still buy some of the algebra books anyway because they have more things to learn."
I'm sure that things like Ebay are gamified... I have a friend who collects records, and reviews them, and has mentioned this
no HS kid is worried about building up his reviewer cred... that is an example of something else
here's an actual review, way way down the list "As a math tutor, I didn't think I could find a geometry textbook I disliked more than "Discovering Geometry" by Michael Serra. Then I encountered this book. (Specifically, I have seen the Michigan version, but in my experience, the state-by-state variation among different versions of the same textbook is relatively minimal.)
Geometry by Larson, Boswell, et al is disorganized and dizzying. In short, it is simply horrendous. It is so replete with dazzling colors and uninformative pictures that I can hardly find the theorems. (It's like a "Where's Waldo?" of theorems and postulates.) By contrast, the Jurgensen book may look dry, but the authors conservatively reserve color to bring your attention to the most important information, such as theorems you should memorize. Larson, et al could really learn something from the more "boring" geometry books of years past.
Just do a chapter-by-chapter comparison with the Jurgensen book, and you'll realize how much material is omitted from this book that used to be taught in geometry classes 20 years ago. This book is poorly organized and doesn't even teach basic constructions. Also, the index, glossary, and other reference materials at the back of the book are not very well formatted. Anyone comparing this book with an older geometry book from 15 or 20 years ago will understand why many veteran math teachers bemoan such watered-down textbooks and why so many college freshmen have to take remedial high school mathematics to learn topics they should have covered before entering college.
Furthermore, it weighs a ton! I chalk that up to the number of useless photographs and graphics in it. (I find the quality of a textbook is inversely proportional to the number of fancy pictures in it.) The Jurgensen book packs far more information in it at a fraction of the weight or volume. The book is a prime example of a textbook with a lot of filler and very little meat. In short, I would never recommend this book to a student."
sounds like this tutor wants the classical axiomatic approach... theorems as stepping-stones to more theorems, and Euclid help you if you don't get one of them
most high school geometry books I looked at were very watered down
even the ones praised by reviewers as "old school" and "rigorous"
oh another book to look at is Lockhart's book Measurement, which is not by any means a textbook but has nice narrative and some fun problems
that looks like it fits within my belief system, yes
at the end of the day, any text is a linear presentation, and that is a disservice to just about any subject. We need hyper-textbooks.
he was teaching undergrad-level elective problem solving courses, and did a bunch of little studies on his students
where he was basically shocked that despite having done high school geometry 2-3 years before, his students had no idea what the basic point was
and couldn't solve even the most basic non-trivial problems
didn't understand that constructions could be part of a proof process and vice versa
the yellow ring is not a yellow plane... perhaps we mis-communicated
I applied your quaternion to the default ring mesh, which lies in the XY plane
@Scott order of orbits if I just go in order taking cross products:
red,blue,orange,yellow,apple,olive,rose,black,sand,lavender,,,navy,
turquoise,,green,purple,,,sulfur,,,,,,,,,,,,,,,,cinnamon,,,,,,,,,,,
,,,,,,,,,maroon,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,spruce,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,coral,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
if there's a blank between commas that means there's no name for it
blue,red,yellow,orange,green,purple,apple,black,olive,rose,,sand,
lavender,,,,navy,turquoise,maroon,,brown,cinnamon,,,,,spruce,,,,,
,,,,,,coral,,,,,,sulfur,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
@Scott at some point I can try separating vectors from bivectors in 2 different arrays, and only combine like with like
wow, that's remarkable. The second order looks much more like what I would expect... and makes me curious about those gaps before navy
but I'm missing something... how does yellow arise from taking blue X red? That would imply that some pair of a red and a blue strut are both orthogonal to a yellow strut. Yellow lies in a blue plane, but it certainly does not lie in a red plane. What am I misinterpreting?
@Scott remember we are doing all 120 icosahedral symmetries for each pair, and adding any uniques to the list
@Scott just doing combinations of the first 30 entries in the list creates 7171 total list entries
I trust you can emit the list of the prototypes themselves
then we just need a function to generate the quaternion from [0,0,1] to each one, and we can render them all as rings
and we really want to paint the "dots" on the sphere too... I'll work on the 3js for that
@Scott I'm planning to draw these just as lines instead of as rings
but we can draw them as rings etc. in 3D as an initial explanation
I think if there are thousands of lines the 3d version might break down a bit
@Scott I think for the dots it would be cool to draw (sections of) cones through the origin
so it would basically be a little wall parallel to the ring / perpendicular to sphere surface
could also maybe do a translucent same-color stripe under each "plane" ring
working on the cones for now... the geometry is annoying. The apex is not at zero
the cone can be open or closed... I liked them better closed, but I'll show you open
@Scott it's fun to zoom through the outside of the sphere
at this cone radius, we could use cylinder just as easily, and then they would start at the sphere, rather than an apex at the center
oh, by "cone" I didn't actually mean draw all the way to the middle
well, they don't give me an option with these simple prebuilt geometries
can't do it without a custom Geometry... too tired tonight
one thing... the order of your blue-first list depends on which blue orbits you happen to cross first, right? That is, the order of orbits could vary
you might look for an order that produces green before orange, for example
I'd be curious to see if that would "pack" the vZome orbits with fewer gaps
@Scott yes, it depends on the order of my "icosahedral_symmetry" function
@Scott there are some other ways to generate orbits, for example by using the red coordinate system and making combinations with small integers
one way to classify the orbits would be to take all icosahedrally symmetrical struts, find the red coordinates and reduce each one to lowest terms [may need to figure out how these transform under scaling by phi, and scale a few times to find the simplest representative], and take the sum of the (absolute values of) coordinates
@jrus I have updated vZomeRenderer to be reactive unless you have set an explicit width
@jrus also, if you downloaded the mis-numbered vZome build 27, please discard and download the new build 36
I now understand your red-coordinate ordering of orbits. It is still somewhat counter-intuitive that there is a unique set of coordinates for every point, but as I think through examples, it starts to feel right. Of course there are many Zome paths to any point, but only one red path (up to reordering, which only affects the 3D embedding of the 6D coordinate system).
@Scott not sure whether there's a fast algorithm for, given a particular point in space (under floating point coordinates) find the nearest zome points with lowest red strut distance
I should figure out the formula for computing Euclidean quadrance in terms of red strut coordinates
@jrus Vince Matsko and Phil Webster were just here for a short visit. I showed them a couple of the notebooks.
I'm storing some books for Vince for a few months while he travels
They've both be doing some divesting associated with moving. Vince gifted me with some nice books, and a twisty puzzle.
Vince said it would help me understand how origami solves cubics
@Scott okay, given a canonical orbit (y, z) representing (1, y, z), the quaternion which takes the vector (1, 0, 0) onto that direction is <r + 1, 0, z, -y> / 2r(r+1), where r = √(1 + y^2 + z^2)
so to compute this we need one square root and one division
@Scott by chance when you were doing the yellow thing were you rotating (0,0,1) rather than (1,0,0) ?
you did. I just never claimed that was the one I was rotating before :)
so I need to do some code organization to deal with different orbits in different colors
not really a big deal. don't worry about sloppiness until you have sorted out correctness
that's kinda what I mean... I want to render the first N orbits of your list, using the appropriate color where defined
so I'll use an array of objects, like { orbit: [y,x], color : "green" }
so I don't guess wrong, can you give me the axis-permuted version of your formula?
testing your formula, or rather my attempt at implementing it, but not getting what I expect
Here is my attempt to code your formula. Note that it has a side-effect on the quaternion argument.
Of course, it is all here: https://beta.observablehq.com/d/3981794cbbb9e5e8
yeah. not precisely sure what our quaternion multiplication convention was etc.
@Scott the thing to do is to try applying quaternion multiplication and see what we get
if I return a 3js quat or vector, they are clear enough
@Scott okay, my code is fine for rotating (1,0,0) onto the direction of (1, 0, [2-1])
@Scott so now the question is how do we properly handle rotating 0,0,1 onto the appropriate thing
@Scott ... or we could just use rings in the y-z plane :P
orbit2quatf = function orbit2quatf([y, z]) {
y = grfloat(y), z = grfloat(z);
const r = Math.sqrt(1 + y**y + z**z);
const denom = 1 / Math.sqrt(2**r**(r+1));
return [denom**(r+1), 0, -denom**z, denom**y];
}
sure, I'm happy to just rotate first or after or whatever...
@Scott what's the best canonical orbit to use near (0,0,1) ?
same one you picked for X I guess... just rotate around 1,1,1
I have to mind simon for a sec. then I'll work through it
I had the signs of z/y backwards previously.. depends on whether you multiply on the right or left as canonical
[but of course flipping the sign on the vector part has the same effect]
@Scott here's a version that should work for you hopefully?
orbit2quatf = function orbit2quatf([a, b]) {
a = grfloat(a), b = grfloat(b);
const r = Math.sqrt(1 + a**a + b**b);
const denom = 1 / Math.sqrt(2**r**(r+1));
return [denom**(r+1), -denom**b, denom**a, 0];
}
to get a unit vector pointed in the direction of [a, b, 1]
@Scott cool now just mirror it to all the other yellow orbits :)
yup, turns out my cones have been wrong for blues too
arguably your cones were right and your rings were wrong ;)
their default does make some sense, if you don't have a trackball attached... you would see the ring edge-on, making it invisible
@jrus OK, it is working... blues and yellows all rendered correctly. Reshared
@Scott
orbitnames = Object.entries(golden_icosahedral_symmetry.orbits)
.reduce((acc, [name, {canonical}]) => (acc[canonical] = name, acc), {})
unfortunately only firefox supports Object.fromEntries for now, or it would be even easier
wait, is that the list of orbits as generated from blue cross-products?
I'm interested in the "missing" orbits from that list
I may also use a slider to determine how deep into the list to render
@Scott
{
const orbits = new IndexedSet
const json = JSON.stringify;
const zero = json([[0,0,1],[0,0,1],[0,0,1]]);
orbits.add([[1,0,1],[0,0,1],[0,0,1]]);
for (let i = 0; i < 10; i++) {
for (let u of icosahedral_symmetry(orbits.contents[i])) {
for (let j = 0; j <= i; j++) {
const v = orbits.contents[j];
const uv = wedge(u, v)
if (json(uv) == zero) continue;
orbits.add([[1,0,1]].concat(orbit2(uv)[0]));
}
}
}
return orbits.contents //.map(u => orbitnames[json(orbit2(u)[0])]).join(',')
}
class IndexedSet {
constructor() {
this.contents = [];
this.map = new Map;
}
add(item) {
const itemhash = JSON.stringify(item);
let index = this.map.get(itemhash);
if (index == null) {
index = this.contents.length;
this.contents.push(item);
this.map.set(itemhash, index);
}
return index;
}
}
I think I'd better put it off until tomorrow. For now, I'll just add reds manually
@Scott you can just use all your named colors from icosahedral_symmetry for the moment
maybe shrink your dots a bit if you are going to make the ends solid
yes, it is going to get noisy.... I think your zigzag lines idea will be better
ok @jrus reshared. The rings get pretty crowded, so I added the ability to limit the number of rings while showing all the dots
With all planes up to purple, you can see that almost all triple intersections are covered. Only two, both yellow-green-purple, have no orbit defined.
Some things start to make sense, at least in terms of correlations.
lavender, maroon, olive all appear at green-green-blue intersections
orange, apple, and sand tend to appear together in models... apparently when projecting things to red planes
I wonder if that missing green-green intersection has occurred often.
So here is a task for you @jrus: find five new colors to assign to these missing orbits
Meanwhile, I'm going to check the gaps in the blue-cross-product sequence, to see if these are the gaps
@jrus yes, if I render dots up through number 47 in your list, I cover all the intersections up to purple... every intersection you see in the figure above. This is not surprising, I suppose, given the process for generating the sequence.
This ordering clearly derives from the fact that orange appears before green.
I think we'll see the vZome orbits packed a little tighter into the sequence with green first
which probably means ordering things so that yellow is discovered before red
OK, I'd better stop... experience shows me you may skip over lots of this. 😉
PS the mouse thing is super frustrating in safari. it goes slow most of the time but sometimes jumps a whole bunch
I'm going to add another parameter, so we can render more rings easily
@Scott PS it's also fun to remove the sphere and cones, and make the rings go from like 70–85
I'm sure you've noticed... the chiral orbits are only rendered one-handed
Hmmm... wouldn't you expect the achiral orbits (that lie in blue planes) to get generated before the chiral ones, since blue is the first in the list?
does orbit2 know when a canonicalized vector lies on the walls of the domain?
I'm going to publish. I know a few Synergetics fanboys who will go a little berserk, probably.
check my prose, see if I've missed anything important
I feel like I don't deserve sole credit for this "design"
I started thinking that I really wanted the blue planes emphasized, then the idea went from there
could do the cones too, but I think I need to stop fiddling
I think if we add a (relatively narrow) stripe parallel to the sphere plane, it will help
@Scott you might want to make 2 sliders for # of orbits with rings and # of orbits with cones
I also want to (someday) have orbit checkboxes, both for ring and pole
Sliders may be too interactive... there is a lot of rendering to wait for. +/- controls?
oh shit. can't handle sliders, because dragging gets stolen
@Scott sometime in the next couple days I would like to try just rendering the planes you get from intersecting lines, and the lines you get from intersecting planes
I think they will mostly end up as the same set, but maybe in a better order
I was thinking of playing with your code to see if I couldn't tease out yellow before red
and also do both ordered by red strut taxi distance to the minimal representative of an orbit
and then render just lines, thousands or more, all in the same hue, varying from super pale to dark and rendered back to front
(probably with a choice of whether to center the projection at a 2, 3, or 5 vertex
actually would be interesting to see how many triangle centers we can construct (ones relevant to spherical geometry; not sure how those compare to the planar case)
yes, was wondering the same the other night. I've told you about the spruce orbit. I suspect it may be the centroid
"""In particular, for a spherical polygon, the moment is the half the sum of (a x b) / ||a x b|| ** (angle between a and b) for each pair of consecutive vertices a,b. (That's for the region to the left of the path; negate it for the region to the right of the path.)"""
[from some stackoverflow comment; not sure that’s actually right]
http://www.demonstrations.wolfram.com/FateOfTheEulerLineAndTheNinePointCircleOnTheSphere/#more
whew, back from another long volleyball night... match and then Lia had to ref for Varsity
(I often felt better about a few really satisfying hits than winning per se)
the sets are pretty variable, so nobody gets great kills
she had a nice block, but the ref missed the hitter's touch after... so they lost the point
yeah. good passing and an amazing setter makes a big difference. we were never super consistent
amazing how much players can develop in a year or two
my Facebook post is doing OK... 200-some reach, four shares... not too bad
lol, FB is trying to get me hooked on "boosting"... offering a credit to boost this post
I'll have my usual conf call in 10 minutes... hopefully <60 min this time
I should go back to trying to figure out this red strut coordinate system
works for me. I'm going to keep doing research on the trackball... there has to be a way to listen to just the canvas element
I need to make myself some better cheat sheets of basic facts
oh yeah, I can never remember those things either... though I'm seldom doing anything on paper
most of my calculation has been with code, in Java, in vZome... a much slower feedback loop than in Observable, harder to debug
Observable is like having a REPL, but where you have better control of what is kept around
so I'm enjoying it as a way to experiment with code, although you have been coding a lot more of the math, obviously
there's really not that much stuff in my math code. you could easily replicate it in short order if you needed I'm sure
pretty much the hardest bit is:
grinv = function grinv(a) {
const [a0=0, a1=0, ad=1] = a;
return simplify3((a0 + a1)**ad, 0 - a1**ad, (a0 + a1)**a0 - a1**a1); }
wtf? I think Chrome just did a self-update tonight... entire look is new
subtle differences of light gray are not good on my monitor
@Scott a properly characterized display often helps a whole lot with subtle grays
i.e. having the software know what your display is putting out
though windows color management was pretty bad last time I used it seriously (10 years ago)
I'm on Mac, but I've never learned anything about characterizing the display in any case
next time we screen share perhaps you can give me tips
unfortunately most people use hardware devices for characterizing displays
so doing it by eye doesn't have the best software available
there was a nice program I used for it a decade ago, but I don't know if it still exists
I wonder if https://displaycal.net is any good
it seems to talk about "which colorimeter" you are using
@Scott it's also quite possible that your display is well characterized and chrome's update just sucks :)
I'm also old and set in my ways, no matter how I strive to stay flexible
I think I'm less change-averse than many of my peers... but who knows
I'll take credit for embracing Javascript, and leave it at that
I think people are way over-reliant on technology, end up using it as a crutch and permanently weakening themselves: voice assistants, smartphones, televisions, cars, strollers, handheld calculators, dishwashers, stiff padded shoes, comfy office chairs, facebook, ...
on the other hand there is a lot of amazing new technology that really opens new possibilities
Now, I go to weaken my ability to sleep on the floor, by sleeping on my memory foam mattress. 😉
so, as any economist will tell you, you can't really avoid utility
@jrus I'm on p7 of Wildberger's paper on Projective and Spherical Geometry... good stuff. It is remarkable how simple it all becomes in the projective view, particularly with rational trig
wow... finished it (skipping all the proofs). I see why you sent it to me
I wonder if it can be made to work for S3... you'd lose the duality
@Scott at some point I calculated all the spreads for angles that can be made with default zome struts. we could also calculate all the dihedral ones, etc.
I think wildberger makes the pentagonal stuff overcomplicated
well, I guess in this paper he sort of derives the golden ratio from the setup he defined, so maybe that was his intent... interesting that he never mentions it by name! Avoiding the crackpots? Or allegations of crackpottery?
wildberger certainly doesn't care about whether he gets labeled a crackpot
that happens anyway due to him saying stuff like "there are only finitely many numbers, therefore all of the questions about whether there are infinitely many of any particular type have trivial resolution"
well, he can count me as a fanboy anyway... fun to read a spherical trig paper with effectively no sin, cos, pi
@Scott check out the spherical trig sections of hestenes's works if you want more practical tools (IMO)
some author I could barely read said that he could barely read it, or something like that
I feel like I need more hands-on experience with GA before I'm ready for that
RT does start to feel natural... I cannot recite the formulas, but the concepts of spread and quadrance are easy to grasp
Here's an application I have in mind (in the math sense):
the problem with the RT thing is that it basically squares everything repeatedly
given the vertices of the 120-cell as vectors, construct the topological model... create the edge, face, cell lists
but I don't think it's a good language for general proofs
I started to try this once, but failed. Every surtope (vertex, edge, face, cell) defines a subspace, so it would seem to be a natural fit for GA blades.
yes, you can represent the n-volume that way if you want to
But how seed the process, finding edges? There is a single minimum quadrance that is shared by all edges.
if any pair of vertices have that quadrance, you've found an edge.
now move up to finding faces... sure, you can "cheat" and note which edges share vertices, but that will just give you paths. If you want to find faces you have to limit the paths to those in the same plane (2-blade)
but is there some equivalent "minimum quadrea" you could use to find the edges sharing a face, analogous to how we found the edges?
I don't exactly understand what you are trying to do; I can certainly believe that this method is a pain in the butt for it
recover the topology of the 120-cell from vertex data... or any regular 4D polytope (or 3D or 2D even)
even better would be to recover the topology of non-regular polytopes, but I'm not going to worry about that
when you say "topology", you mean which vertices are adjacent to which edges/faces/... ?
edges as pairs of vertices, faces as lists of edges, cells as lists of faces
but if you talk to computational geometry people I wouldn't be surprised if they have some good ideas
the other possibility is to derive the topology as you generate the vertices using Wythoff's procedure
the GA angle: I was hoping that some product (the join?) of two surtope blades (say, two edges) would produce the next level of surtope (the plane containing the face)
if you do a ^ b, where a and be are arbitrary blades, you'll get something which tells you what happens when you make something like a generalized parallelogram out of them
but that doesn't necessarily help you with some arbitrary shape
and the parallelipipeds are just representations of oriented areas and volumes
so I feel like one such oriented volume would be the volume of a dodecahedral cell
I think I may play around with this in Observable, just for the 2D case, using Ganja
at least generate all the products given the edges of a heptagon, or something
@Scott definitely try generating all the clifford products of various heptagon diagonals
well, for now it is another "someday" project... I want to get some closure on the vZome stuff
though that particular one would be about one night's work
I'm trying to figure out what a good basis is for multivectors given a red strut basis for vectors
especially if I fork your math notebook and adapt to the heptagon field
hmmm... you'd better have a whiteboard when you explain it to me
@Scott I should make some notebooks where I try to explain golden field arithmetic in a bit more comprehensible way
yes, I was just thinking we need to catch up on the explanatory prose
right now I'm trying to figure out how to make the vZomeRenderer into something more readily reconfigured
it's helpful to have a cheat sheet with various stuff like ϕ – ϕ⁻ = 1, ϕ + ϕ⁻ = ϕ² – ϕ⁻² = √5, ϕ² + ϕ⁻² = 3 ϕ³ – ϕ⁻³ = 4 ϕ³ + ϕ⁻³ = 2√5 ϕ⁴ – ϕ⁻⁴ = 3√5 ϕ⁴ + ϕ⁻⁴ = 7 ϕ⁵ – ϕ⁻⁵ = 11 ϕ⁵ + ϕ⁻⁵ = 5√5 ϕ⁶ – ϕ⁻⁶ = 8√5 ϕ⁶ + ϕ⁻⁶ = 18
I had forgotten how easy it is to generate integer lengths
That's another thing I used to think about, but long ago abandoned... ways to "simplify" numbers in this field (or others too)
as you're doing here... only sums of powers of ϕ with small (or unitary) coefficients
is that "phinary" numbers? a unique representation with only coefficients of +/- one?
well these are indeed "phinary" representations of these numbers, but no that's not the whole idea of that one
but these come up in the various spreads related to zome struts
I'm not sure if they come out of the integers, or the sums/differences of powers of ϕ/ϕ⁻
they come up a lot when you try to invert various golden rationals
it's easier to do the arithmetic if you can just pluck some integers out
@jrus I've added support for panels in VSON export, and soon I'll add panel support to the Observable code
@Scott apparently the oldest yellow struts were decidedly greener than even slightly-less-old ones
but anyway, I'm still going to try to (sometime hopefully not too long in the future) make a hue/value categorical color picker
and we can try to put the standard zome struts as close to their original colors as possible, and then pack other colors around those
somehow over time zometool manuals got both less charming and less useful
I should really take the staples out and feed it through someone's scanner
going out soon... dinner and bowling with Lia's volleyball team and parents
kind of stalled doing some refactoring in vZomeRenderer
@jrus I have finished some refactoring of vZomeRenderer, adding support for panels
I have also incorporated your improved resolveSegments
vZomeRenderer is now a simple convenience function, calling things like meshGroup and defaultCanvas
I pushed to get the panel support in mostly so I could send my parents a 60th anniversary card in Observable
Jacob, what would you consider your best source on quaternions? Is there a particular book or article that you would credit?
@Scott no idea, sorry. that conway one is probably alright. I have it somewhere
They are clear, but very terse... Close to the opposite of 3 blue 1 Brown
to be honest I find thinking about quaternions as scalars + bivectors a lot clearer
Ok... Yes, the GA material is pretty clear, since they always want to demonstrate how it subsumes C and H
@Scott I find it's a lot clearer to think of complex numbers that way
in patricular, there are a few different types of plane-related objects we care about: Euclidean affine points, displacement vectors (difference between points) and other kinds of Euclidean vectors (velocity, acceleration, etc.), complex numbers (quotient of two vectors)
if we pick out an arbitrary point in the plane o, and then we pick out a particular unit vector direction x, and we pick a particular orientation I for the plane, then we can represent every point in the plane using coordinates o + x(a + Ib), where a and b are scalars
@Scott but I find it's pretty helpful to remember that a + Ib is a complex number, x(a + Ib) is a vector, and o + x(a + Ib) is a point. They can all be represented by the same pair of coordinates (a, b), but they are distinct types of objects
@jrus I see that a+Ib and x are clearly different kinds of multivectors. But isn't the distinction between o and x (or o+x) just a convention, not a true difference? That is to say, what makes o structurally different from a vector?
when you take the difference between two points you get a displacement vector
you can add affine combinations of points together to get another point
e.g. you could take 1/2(p + q), and that would be the midpoint between p and q
but that would work with p and q as vectors relative to (0,0,0) also
I think I see... you can observe this distinction when doing math with pen and paper, and you can even encode the distinction in a type system
if you have a point p in the plane, a point o, a "unit vector" x, and an orientation I, then you can represent p in the form o + x(a + bI)
yes, I got that, just still trying to shed my ingrained notion that N coordinates can equivalently be treated as a point or a vector in N-space
it so happens that in a computer we generally need to stick some coordinates in there
@Scott I'm channeling hestenes here. generally mathematicians set things up so that Euclidean points "are" vectors
sometimes they make it so that points "are" vectors which happen to lie on a subspace not passing through the origin
@Scott it's not really about "useful" so much as "try to not confuse yourself about what is inherent vs. what is an arbitrary imposition for convenience"
this business of affine combinations is curious, too... 1/2(p+q) is clearly the midpoint... but what is (p+q)?
well that depends on how you define + and what kind of model you are using
I guess that is the utility I was referring to... preventing confusion
in which case points are really proportions, and any multiple yields the same thing
sometimes people use "weighted points" where the weight has some meaning
yes, I've done that for years without a deep understanding
learned it when I took my first course, with a 4-pen plotter
it just means that we are using 4-proportions to represent 3-dimensional points
based on where they project onto the subspace where w = 1
you could alternately project them onto half of a 3-sphere or something if you wanted
the convenient thing about homogeneous coordinates is that any perspective projection becomes a linear transformation of the ambient space
Ok, I'll keep reading... gotta shed my Cartesian bias
I'm working on generating VSON output for any 4D polytope that vZome can generate
where we don't have any way of saying that there is a correct coordinate system
so we'll have some nice data sets to play quaternions with
if you do, can I borrow your Hestenes? I can offer a "prisoner exchange". 😉 Vince gave me a couple of nice books you may not have seen.
feel free to say "no"... I know that is asking a lot
I tend to buy books I want to read carefully (or even many I don’t), but it can be handy to skim through first
I always like reading Baez. Had a nice Skype session with him once
he had used vZome a little for one of his articles, and I offered to show him the 4D polytope generator
"A torsor is like a group that has forgotten its identity."
I'm prepping a notebook for presenting this afternoon
I'd like to refine it a bit more... like combining the "phiPower" and "scale" cells into one control that directly emits a golden rational
hey @Scott. sorry, was feeling tired today after a very long day yesterday, and just stayed home and rested
we're driving to southern CA for a couple weeks starting tomorrow
yao has a couple of conferences near san diego to attend, and we're going to spend other time near LA in my parents' house (they are in mexico)
I'll try to keep a little momentum with the notebooks, but we'll see
I also watched the ford/kavanaugh hearings yesterday/today. oof
I would urge any american who can spare the 6 hours to watch them directly
watching some short snippets really doesn't do it justice. K. went through a 3-hour tantrum
it reminded me very strongly of the 2-year-old I saw sobbing and struggling on thursday after his mother pulled him away from trying to push a girl off a rocking horse
of course, the 2-year-old's tantrum only lasted for about 5–10 minutes
you can't be a supreme court justice without more stamina than that
lots of them queued up... one of Wildberger's right now
@Roice forgot to tell you... I did make a start on porting clif4d to Observable, but it takes a lot more work than I realized. In particular, I need to get three.js to use the custom shaders.
Three.js is not so easy for doing text labels, so I encoded the indices with cone radii
@jrus these are your indices. Next step will be to recreate the net as SVG.
Here is a net whose symmetries align with the indices. It will be easier to code.
Going to code this up in Observable with D3... Good excuse to learn D3
your Slack feed should be full of it... I guess that's the downside for the free plan
sorry, thought you had all this stuff waiting for you
was I right, you don't get it unless you scroll up explicitly?
distracted me from my Celebration of Mind presentation
is that actually the order that my quaternions result in? I hadn't tried writing them down on a physical model :)
and I still want to code the mapping, so this gives me firmer footing
not sure whether I would actually consider the immediately adjacent gray/white triangles to be the same number, or instead have the same number assigned to ones opposite each-other on the sphere
I figured that the consistent text orientation gave a clue that these are the same quaternion
you can see I did something very different in my first physical model
well what I mean is, all your white triangles seem like the labels I had
I think I might have the gray ones mirrored across. but that might not be as easy to interpret
oh, also, to be clear, the photos show your indexing with blue triangles, but I switched to gray to differentiate
I'm going to try to "spoil" Linear Algebra for middle schoolers in 6 minutes... eigenvalues, SVD, non-invertibility
all the stuff I had to learn without any visual aids
I didn't really do much, but I did spend a bunch of time trying to work through http://geocalc.clas.asu.edu/pdf/DLAandG.pdf
@Scott this is the original paper about the so-called "conformal geometric algebra" model
these are somewhat tough ideas. I'm not sure what the best way is to make them accessible
well, tough for me anyway. I'm sure there are some mathematicians who think this is all trivial
yeah, starts with outermorphisms on page two... and not a single picture
"outermorphism" just means take a linear transformation of vectors and apply it to multivectors as well
yes, I should know that... was wading through GA4P last week
i.e. where you decompose a multivector into some sum of outer products of vectors, apply the linear transformation to each vector, and then take the outer products and sums again in the same way
a Math professor friend of mine sent me some GA papers a while back... Fontijne's thesis is pretty approachable
next week I go back home to IL and IA for my 35th college reunion. I've lined up a meeting with that same professor friend, Alan Macdonald. He started teaching at my college when I was a junior... then got into GA some years later.
Pretty standard stuff, I'd say... denser than I like for casual reading, but probably acceptable as a math text.
I know hestenes has a bit of an issue, in the sense that macdonald's book starts from a linear algebra perspective, and kind of bolts GA on top of that
instead of starting with GA and extracting linear algebra from it
but probably pedagogically reasonable in a typical college course
I poked Alan with the Wildberger stick, when I told him I was coming. He agreed to meet anyway, then proceeded to refute NW 😉
@jrus I finally finished my 4D VSON export tool... we can now play with rotations and projections of the 120-cell: https://gist.github.com/vorth/53ea8016f6df2ea27c0aa653a4c63cd9
of course, we'll need to handle unknown orbits as soon as we do any odd rotation
@jrus I have updated the main rendering notebook to deal with unknown orbits
Here is my new notebook exploring rotations of the 120-cell: https://beta.observablehq.com/d/9ee20664107a443d
The rotation it is showing is an interesting one... it appears to have rotated an "odd" dodecahedron along a red axis, replacing the "even" dodecahedron at the center. Therefore it still uses some red and blue struts, but all other edges are now misaligned with the Zomeball, as if they were reflected in a red mirror.
Hmmm... actually, every rotation I do gives me the same configuration of vertices... a central dodecahedron, etc... just oriented differently with respect to the ball orientation. This is roughly what I remember from my earlier explorations with two-sided multiplication years ago.
@Scott are all of those struts supposed to be filled in in your notebook? or only a scattered few?
I guess when they go outside of explicitly named directions there are no struts to draw
@Scott so anyway, this is not any arbitrary 4D rotation of the shape, right? You are only doing a 1-sided quaternion multiplication
I think if you do a 2-sided multiplication you'll get different shapes
(unfortunately for most choices of 2 quaternions you'll get no struts drawn in)
unfortunately I cannot experiment easily right now... my Chrome has decided it cannot do WebGL.
I'm going to put in the code for automatic shapes, and then eventually we can generate colors for them too
@Scott oh, but quattransform is just a way to do a 3-d rotation
if you want a 4-dimensional rotation you need to specify an 8-dimensional thing
(some possible rotations are double rotations in 2 planes)
I'm not sure what the best way is to represent arbitrary rotations for zome coordinates even in 3d though
@Scott the other thing you could do is take a list of vectors, and then reflect in them sequentially
Reshared. Conway and Smith say the general orthogonal map in 4d is a two sided quaternion multiplication.
yeah. this just isn't a very enlightening representation (from what I can tell)
*Thread Reply:* Are you referring to the orthogonal projection? Or the Q&O book?
Well, spent the day upgrading from Yosemite to El Capitan... I thought it was stuck, but it just took hours.
The good news: computer is not dead. The bad news: Chrome still won't do WebGL.
@jrus I have reshared https://beta.observablehq.com/d/9ee20664107a443d
I've been playing with different L and R quaternions, and I have implemented a fairly good approximation at "hidden cell removal".
The current state of the notebook is showing you the "green quaternion" one-sided rotation, producing the vertex-first projection of the 120-cell. Without the hidden cell removal, you would see 8 yellow struts in the center, and the dodecs would be hard to pick out
@Scott between the dog's eye surgery and the smoke and election news and general distraction I haven't been doing much useful
did you see jashkenas's notebook from august? https://beta.observablehq.com/@jashkenas/california-fires
I was daydreaming of making a map mashup... my parents are headed toward Sacramento on the California Zephyr (Amtrak) right now
but a lot more daydreaming about various controls re: vZome... orbit picker, number control, quaternion control, etc.
I should sit down and try to work out how to represent red-coordinate-system symmetry-preserving rotations
I'll have my folks for a week, but I want to keep what little momentum I have on Observable. I've been talking it up with everyone I know.
what about figuring out geometry for new strut directions?
I've been meaning to translate this to Javascript... a pretty good generic parallelogram prism, independent of the algebraic field. It uses only integer coordinates. You're welcome to ignore it, of course.
I think you should find something that you'll enjoy, regardless of utility. Of course, I think enjoyment and utility often correlate for both of us.
Sorry, it uses only rational coordinates... I threw in the 1/10 scale to have it match the usual scale of Zome struts.
@Scott is this trying to fit struts into pentagon/rectangle/triangle holes in the nodes?
no, no attempt to match any particular ball geometry... just focused on a reasonable cross-section to balance with modeled struts
Yes, it would be nice to have something that looks modeled
you also have a generic strut drawing thing that matches the ball shape though right?
no, nothing that matches ball shape... everything like that was modeled in vZome
these days I stick to a roughly golden rectangle cross-section when modeling something new... I just translate the nearest blue face to the strut centerline, offset out from the ball. You could apply the same principle with "no-twist" pentagon and triangle prisms, or with decagon and hexagon prisms, computed once.
@jrus I have recreated the vZome orbit triangle: https://beta.observablehq.com/d/1ffda0d4a9ec4bec
@jrus I have done some work to combine a vZome view with an orbit triangle
hey @Scott sorry haven't been using this computer much. just noticed these. got some work done on my spline thing recently. haven't been looking at the zome stuff
hi @jrus. You should check out my last notebook... I think you'll enjoy it
I've been spending a bunch of time thinking about spline stuffs the past week or two
he left his job at google a while back so has some time to work on this spline stuff again (which he mostly set aside for the past decade)
I have some proper ideas about finishing up the missing parts of my Apollo curve notebook/paper
and he has a pretty fruitful alternative line of ideas that I think could be pretty practical as a general-purpose tool in applications like illustration or font design
I've been largely inactive, except for the bit of Observable work I mentioned. I've also started a small in-vZome project, to improve strut design
he's mostly doing well, but one of the meds had him peeing much more than usual without waiting to go out, which was kind of annoying...
oh it was obvious that he had one cataract, and it was obvious that he couldn't see much
one got replaced with a plastic lens. the other eye they just removed the lens
Hey @Roice, FYI, today I'm working on porting clif4d to ES2015 and three.js and Observable
@channel I have published a new notebook about finding golden numbers in the interval [0,1]. I plan to use these functions to "snap" 4D floating point rotation matrices or quaternions to golden number equivalents.
@channel I have also published the Clifford torus notebook that @Roice and I worked on: https://beta.observablehq.com/@vorth/clif4d-a-track-torus
@Scott I didn't realize, apparently I was on a join.me "pro" trial which has now ended
not sure there was actually any different feature. I think that message might just be made up
well, I did have the use of "@vzome" or something as a channel name, for a month, when I first signed up... but that was 2-3 years ago
I think they tried to hook me again recently, by reviving that trial, and the channel name, for 30 days
@channel I have been playing with the stereographic projection for generating golden vectors that lie on a circle: https://beta.observablehq.com/d/be0be1908e0c1255
So far, it is fun but ultimately still disappointing. It appears that I cannot generate "nice" vectors... I miss all the blue-plane orbits. Just look at those denominators!
@Scott you want to use arbitrary golden-rational points on the line
even so I think it is impossible to come up with a circle point that is on a slope of 1/phi, for example
looking at my inverse projection, if I want a ratio of 1:φ, then I need a square root of 2+φ... which is not a perfect square
oh that's true, yeah you actually want to have arbitrary points such that their square is a golden rational
I have to play with simon. I'll maybe think about it a bit later. no promises though
@Scott maybe better to skip the stereographic projection per se and just rotate & scale the vector (1, 0) to point at an arbitrary (x, y)
but it was a fun exercise... wouldn't have guessed I could draw a circle
neat how the stereographic projection is invertible without sqrt, whereas the gnomonic is not
so one could do inverse stereographic with rationals
@Scott yeah, any time you have purely rational points on a conic section, you can map them to rational points on a line. no need for square roots
@Scott this is why https://en.wikipedia.org/wiki/Conformal_geometric_algebra is such a neat tool
“fractional linear transformations” (“Möbius transformations) like circle inversions, etc. can accomplish a lot
nice. Yes, was thinking of the conformal GA model earlier. Not ready to go there yet, but I do need to find a different approach for my 4d-polytope rotation project
today I have shifted gears, to port my "white strut" geometry to Observable
https://beta.observablehq.com/@vorth/a-shape-for-white-struts
oh, maybe https://beta.observablehq.com/@vorth/exploring-4d-quaternion-rotations are now updated, etc?
@jrus just published another take on the orbits sphere: https://beta.observablehq.com/@vorth/vzome-icosahedral-orbits
I still want to do the SVG gnomonic projection, with the ability to toggle poles/circles on and off. This will do for now
I was interested in seeing which intersections lead to some of the white orbits in my other notebook.
Yes, I don't have an easy way to truncate those cones.
I may ask you for some new colors, to assign to a few of the new orbits
Never mind... seems there is little correlation between the white orbits used and the intersections rendered above.
Hey @Roice… want to port some of your stuff to Observable? This might help: https://beta.observablehq.com/@renatoppl/hyperbolic-plane
Still just getting set up, listening in . I like the name observable.
@Jacob Kandel Hi Jacob I am going to have a look at "forking?" your 4D polytopes observable page if you don't mind.
@Chris K. Palmer has joined the channel